feat: marketplace modules and isolated container management
This commit is contained in:
@@ -19,13 +19,14 @@ export class ApiError extends Error {
|
||||
|
||||
/** Liest das CSRF-Token aus dem nicht-HttpOnly-Cookie. */
|
||||
function readCsrfToken(): string | null {
|
||||
let csrfToken: string | null = null;
|
||||
for (const part of document.cookie.split(';')) {
|
||||
const [name, ...value] = part.trim().split('=');
|
||||
if (name === 'mpm_csrf') {
|
||||
return decodeURIComponent(value.join('='));
|
||||
csrfToken = decodeURIComponent(value.join('='));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return csrfToken;
|
||||
}
|
||||
|
||||
/** Callback, der bei einem 401-Fehler aufgerufen wird (Auth-Context setzt ihn). */
|
||||
@@ -63,11 +64,14 @@ export async function apiRequest<TResponse>(
|
||||
method,
|
||||
headers,
|
||||
credentials: 'same-origin',
|
||||
cache: 'no-store',
|
||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||
signal,
|
||||
});
|
||||
|
||||
if (response.status === 401 && unauthorizedHandler) {
|
||||
// A 401 from a feature endpoint may be request-specific. Let the auth
|
||||
// context verify the session through /auth/me before clearing the user.
|
||||
if (response.status === 401 && path !== '/api/v1/auth/me' && unauthorizedHandler) {
|
||||
unauthorizedHandler();
|
||||
}
|
||||
|
||||
@@ -92,4 +96,4 @@ export async function apiRequest<TResponse>(
|
||||
}
|
||||
|
||||
return (await response.json()) as TResponse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,11 +121,63 @@ export async function revokeModuleAccess(userId: string, moduleId: string): Prom
|
||||
|
||||
/** Liest das CSRF-Token aus dem nicht-HttpOnly-Cookie. */
|
||||
function readCsrfToken(): string | null {
|
||||
let csrfToken: string | null = null;
|
||||
for (const part of document.cookie.split(';')) {
|
||||
const [name, ...value] = part.trim().split('=');
|
||||
if (name === 'mpm_csrf') {
|
||||
return decodeURIComponent(value.join('='));
|
||||
csrfToken = decodeURIComponent(value.join('='));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return csrfToken;
|
||||
}
|
||||
|
||||
export interface MarketplaceProvider {
|
||||
provider: 'github' | 'gitea' | 'forgejo';
|
||||
label: string;
|
||||
configured: boolean;
|
||||
connected: boolean;
|
||||
accountLogin: string | null;
|
||||
baseUrl: string;
|
||||
callbackUrl: string;
|
||||
}
|
||||
|
||||
export interface MarketplaceRepository {
|
||||
owner: string;
|
||||
repository: string;
|
||||
htmlUrl: string;
|
||||
description: string;
|
||||
defaultBranch: string;
|
||||
installed: boolean;
|
||||
}
|
||||
|
||||
export async function fetchMarketplaceRepositories(provider: MarketplaceProvider['provider']): Promise<MarketplaceRepository[]> {
|
||||
return apiRequest<MarketplaceRepository[]>(`/api/v1/marketplace/repositories/${provider}`);
|
||||
}
|
||||
|
||||
export async function installMarketplaceRepository(
|
||||
provider: MarketplaceProvider['provider'],
|
||||
owner: string,
|
||||
repository: string,
|
||||
): Promise<Module> {
|
||||
const response = await apiRequest<{ module: unknown }>(
|
||||
`/api/v1/marketplace/repositories/${provider}/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/install`,
|
||||
{ method: 'POST' },
|
||||
);
|
||||
return moduleSchema.parse(response.module);
|
||||
}
|
||||
|
||||
export async function fetchMarketplaceProviders(): Promise<MarketplaceProvider[]> {
|
||||
return apiRequest<MarketplaceProvider[]>('/api/v1/marketplace/providers');
|
||||
}
|
||||
|
||||
export async function beginMarketplaceConnection(provider: MarketplaceProvider['provider']): Promise<string> {
|
||||
const response = await apiRequest<{ authorizationUrl: string }>(
|
||||
`/api/v1/marketplace/connections/${provider}/start`,
|
||||
{ method: 'POST' },
|
||||
);
|
||||
return response.authorizationUrl;
|
||||
}
|
||||
|
||||
export async function disconnectMarketplaceProvider(provider: MarketplaceProvider['provider']): Promise<void> {
|
||||
await apiRequest(`/api/v1/marketplace/connections/${provider}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user