laster 0.0.2

This commit is contained in:
2026-05-26 15:43:56 +07:00
parent e2c4881bb7
commit 8ceb1bb1df
24 changed files with 583 additions and 40 deletions

View File

@@ -4,6 +4,9 @@ export const DEFAULT_PACKAGE_BASE_URL = normalizeUrl(
export const DEFAULT_AGENT_BASE_URL = normalizeUrl(
import.meta.env.VITE_AGENT_BASE_URL || 'http://127.0.0.1:5010'
);
export const DEFAULT_APP_OPEN_URL = normalizeUrl(
import.meta.env.VITE_APP_OPEN_URL || 'http://127.0.0.1'
);
export function normalizeUrl(value) {
const text = String(value || '').trim();
@@ -16,6 +19,30 @@ export function joinUrl(baseUrl, path) {
return `${normalizedBaseUrl}${normalizedPath}`;
}
function normalizeOpenUrl(value) {
const text = normalizeUrl(value);
if (!text) return '';
try {
const parsed = new URL(text);
return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? text : '';
} catch {
return '';
}
}
export function getAppOpenUrl(app) {
return normalizeOpenUrl(
app?.openUrl
|| app?.open_url
|| app?.webUrl
|| app?.web_url
|| app?.homepageUrl
|| app?.homepage_url
|| app?.homepage
) || normalizeOpenUrl(DEFAULT_APP_OPEN_URL);
}
async function requestJson(baseUrl, path, options = {}) {
const {
timeoutMs = 8000,
@@ -50,8 +77,7 @@ async function requestJson(baseUrl, path, options = {}) {
}
if (!response.ok) {
const detail = payload?.detail || payload?.error || payload || response.statusText;
throw new Error(`${response.status} ${formatErrorDetail(detail)}`);
throw new Error(`${response.status} ${formatErrorDetail(payload || response.statusText)}`);
}
return payload;
@@ -75,7 +101,27 @@ function formatErrorDetail(detail) {
if (detail && typeof detail === 'object') {
const location = Array.isArray(detail.loc) ? detail.loc.join('.') : '';
const message = detail.msg || detail.message || detail.detail || detail.error;
const messageParts = [
detail.msg,
detail.message,
detail.error,
detail.detail
]
.map((item) => String(item || '').trim())
.filter(Boolean);
if (Array.isArray(detail.missingPackageFiles) && detail.missingPackageFiles.length > 0) {
const missingFiles = detail.missingPackageFiles
.map(formatMissingPackageFile)
.filter(Boolean)
.join('; ');
if (missingFiles) {
messageParts.push(`Missing package files: ${missingFiles}`);
}
}
const message = [...new Set(messageParts)].join('. ');
if (message) {
return location ? `${location}: ${message}` : String(message);
@@ -91,6 +137,17 @@ function formatErrorDetail(detail) {
return String(detail || 'Request failed');
}
function formatMissingPackageFile(item) {
if (!item || typeof item !== 'object') return String(item || '').trim();
const packageName = String(item.packageName || item.componentId || 'package').trim();
const version = String(item.version || '').trim();
const downloadUrl = String(item.downloadUrl || '').trim();
const label = [packageName, version].filter(Boolean).join(' ');
return downloadUrl ? `${label} (${downloadUrl})` : label;
}
export async function fetchPackageApps(packageBaseUrl) {
const payload = await requestJson(packageBaseUrl, '/api/apps', { timeoutMs: 10000 });
return Array.isArray(payload?.apps) ? payload.apps.map(normalizePackageApp) : [];
@@ -185,7 +242,16 @@ function normalizePackageApp(app) {
appName: String(app.appName || app.app_name || app.name || '').trim(),
version: String(app.version || '').trim(),
status: String(app.status || 'Released').trim(),
packageCount: Number(app.packageCount || app.package_count || 0)
packageCount: Number(app.packageCount || app.package_count || 0),
openUrl: normalizeOpenUrl(
app.openUrl
|| app.open_url
|| app.webUrl
|| app.web_url
|| app.homepageUrl
|| app.homepage_url
|| app.homepage
)
};
}
@@ -196,7 +262,16 @@ function normalizeInstalledApp(app) {
version: String(app.installedVersion || app.version || app.package_version || '').trim(),
status: String(app.status || 'installed').trim(),
installedAt: app.installedAt || app.installed_at || '',
updatedAt: app.updatedAt || app.updated_at || ''
updatedAt: app.updatedAt || app.updated_at || '',
openUrl: normalizeOpenUrl(
app.openUrl
|| app.open_url
|| app.webUrl
|| app.web_url
|| app.homepageUrl
|| app.homepage_url
|| app.homepage
)
};
}