Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export interface RetryMessageResult {
succeeded?: string[] | null;
failed?: string[] | null;
outcomes?: RetryMessageOutcome[] | null;
/** Whether the repository was marked as published and left the queue. */
posted?: boolean;
/**
* Present only when that marking failed: the post went out but the repository
* stayed in the queue, so the scheduled run will publish it again.
*/
posted_error?: string;
}

export interface CronJobHistoryResponse {
Expand Down Expand Up @@ -203,6 +210,47 @@ export const retryMessagePost = async (
return response.json();
};

/**
* Publishes a repository immediately to every enabled integration, instead of
* waiting for its turn in the publication queue. Content Maestro resolves the
* integrations itself: the dashboard's cached list must not decide what is sent.
*
* The request is synchronous and can take minutes - the Threads connector alone
* is configured with a 90 s timeout - so callers pass a signal and must treat an
* abort as "may still be running", not as "did not publish".
*/
export const publishMessageNow = async (
url: string,
options?: { signal?: AbortSignal }
): Promise<RetryMessageResult> => {
const settings = getApiSettings();

if (!isApiConfigured()) {
throw new Error("API not configured. Check the Content Maestro settings.");
}

const response = await fetch(`${settings.contentMaestro.apiBaseUrl}/api/message/publish`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${settings.contentMaestro.apiBearerToken}`,
},
body: JSON.stringify({ url }),
signal: options?.signal,
});

if (!response.ok) {
if (response.status === 429) {
throw new Error("Rate limit exceeded. Please try again later.");
}
// Content Maestro answers errors as plain text.
const detail = (await response.text()).trim();
throw new Error(detail || `Failed to publish the repository: ${response.status}`);
}

return response.json();
};

export const updateCronStatus = async (name: string, is_active: boolean): Promise<void> => {
const settings = getApiSettings();
const isConfigured = isApiConfigured();
Expand Down
18 changes: 15 additions & 3 deletions src/components/ui/base/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,19 @@ const DialogOverlay = React.forwardRef<
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName

interface DialogContentProps
extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
/**
* Blocks the built-in close button while an operation must not be interrupted -
* closing mid-run would throw away the only place its result is shown.
*/
closeDisabled?: boolean;
}

const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
DialogContentProps
>(({ className, children, closeDisabled, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
Expand All @@ -42,7 +51,10 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-1 focus:ring-ring-subtle focus:ring-offset-1 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<DialogPrimitive.Close
disabled={closeDisabled}
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-1 focus:ring-ring-subtle focus:ring-offset-1 disabled:pointer-events-none disabled:opacity-30 data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
>
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
Expand Down
Loading