mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix: add impersonation params to resource urls
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
import { building } from '$app/environment';
|
||||
|
||||
const KEY_TARGET_USER_ID = 'console.impersonation.targetUserId';
|
||||
@@ -82,6 +82,31 @@ export function readImpersonationTargetUserId(): string | null {
|
||||
return sessionStorage.getItem(KEY_TARGET_USER_ID);
|
||||
}
|
||||
|
||||
export function createImpersonatedResourceUrl(
|
||||
url: string,
|
||||
queryParams: Record<string, string | number | boolean | undefined> = {}
|
||||
): string {
|
||||
const parsedUrl = new URL(url, globalThis.location?.origin);
|
||||
const targetUserId = readImpersonationTargetUserId();
|
||||
|
||||
for (const [key, value] of Object.entries(queryParams)) {
|
||||
if (value !== undefined) {
|
||||
parsedUrl.searchParams.set(key, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetUserId) return parsedUrl.toString();
|
||||
|
||||
parsedUrl.searchParams.set('impersonateUserId', targetUserId);
|
||||
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
|
||||
export const impersonatedResourceUrl = derived(
|
||||
impersonationRevision,
|
||||
() => createImpersonatedResourceUrl
|
||||
);
|
||||
|
||||
export function readOperatorSnapshot(): OperatorSnapshot | null {
|
||||
if (building) return null;
|
||||
const raw = sessionStorage.getItem(KEY_OPERATOR);
|
||||
|
||||
@@ -3,10 +3,17 @@
|
||||
import { page } from '$app/state';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { HeaderAlert } from '$lib/layout';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
import { actionRequiredInvoices, hideBillingHeaderRoutes } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { getApiEndpoint } from '$lib/stores/sdk';
|
||||
const endpoint = getApiEndpoint();
|
||||
|
||||
function invoiceUrl(invoiceId: string) {
|
||||
return $impersonatedResourceUrl(
|
||||
`${endpoint}/organizations/${$organization.$id}/invoices/${invoiceId}/view`
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $actionRequiredInvoices && $actionRequiredInvoices?.invoices?.length && !hideBillingHeaderRoutes.includes(page.url.pathname)}
|
||||
@@ -14,9 +21,7 @@
|
||||
Please authorize your upcoming payment for {$organization.name}. Your bank requires this
|
||||
security measure to proceed with payment.
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button
|
||||
text
|
||||
href={`${endpoint}/organizations/${$organization.$id}/invoices/${$actionRequiredInvoices.invoices[0].$id}/view`}>
|
||||
<Button text href={invoiceUrl($actionRequiredInvoices.invoices[0].$id)}>
|
||||
View invoice
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { currentPlan } from '$lib/stores/organization';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
|
||||
export let show: boolean;
|
||||
export let mimeTypeQuery: string = 'image/';
|
||||
@@ -82,16 +83,14 @@
|
||||
}
|
||||
|
||||
function getPreview(bucketId: string, fileId: string, size: number = 64) {
|
||||
return (
|
||||
sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.storage.getFilePreview({
|
||||
bucketId,
|
||||
fileId,
|
||||
width: size,
|
||||
height: size
|
||||
})
|
||||
.toString() + '&mode=admin'
|
||||
return $impersonatedResourceUrl(
|
||||
sdk.forProject(page.params.region, page.params.project).storage.getFilePreview({
|
||||
bucketId,
|
||||
fileId,
|
||||
width: size,
|
||||
height: size
|
||||
}),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { type Models, Query } from '@appwrite.io/console';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { selectedInvoice, showRetryModal } from './store';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
import {
|
||||
ActionMenu,
|
||||
Badge,
|
||||
@@ -64,6 +65,12 @@
|
||||
$showRetryModal = true;
|
||||
}
|
||||
|
||||
function invoiceUrl(invoiceId: string, action: 'view' | 'download') {
|
||||
return $impersonatedResourceUrl(
|
||||
`${endpoint}/organizations/${page.params.organization}/invoices/${invoiceId}/${action}`
|
||||
);
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (page.url.searchParams.get('type') === 'validate-invoice') {
|
||||
window.history.replaceState({}, '', page.url.pathname);
|
||||
@@ -155,12 +162,12 @@
|
||||
<ActionMenu.Item.Anchor
|
||||
leadingIcon={IconExternalLink}
|
||||
external
|
||||
href={`${endpoint}/organizations/${page.params.organization}/invoices/${invoice.$id}/view`}>
|
||||
href={invoiceUrl(invoice.$id, 'view')}>
|
||||
View invoice
|
||||
</ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
leadingIcon={IconDownload}
|
||||
href={`${endpoint}/organizations/${page.params.organization}/invoices/${invoice.$id}/download`}>
|
||||
href={invoiceUrl(invoice.$id, 'download')}>
|
||||
Download PDF
|
||||
</ActionMenu.Item.Anchor>
|
||||
{#if status === 'overdue' || status === 'failed' || status === 'abandoned'}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import { getApiEndpoint, sdk } from '$lib/stores/sdk';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { resolve } from '$app/paths';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
import type { PaymentMethod as StripePaymentMethod } from '@stripe/stripe-js';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
@@ -37,6 +38,12 @@
|
||||
|
||||
const endpoint = getApiEndpoint();
|
||||
|
||||
function invoiceUrl(invoiceId: string) {
|
||||
return $impersonatedResourceUrl(
|
||||
`${endpoint}/organizations/${page.params.organization}/invoices/${invoiceId}/view`
|
||||
);
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if (!$organization.paymentMethodId && !$organization.backupPaymentMethodId) {
|
||||
paymentMethodId = $paymentMethods?.total ? $paymentMethods.paymentMethods[0].$id : null;
|
||||
@@ -173,11 +180,7 @@
|
||||
)} has failed. Retry your payment to avoid service interruptions with your projects.
|
||||
</p>
|
||||
|
||||
<Button
|
||||
external
|
||||
href={`${endpoint}/organizations/${page.params.organization}/invoices/${invoice.$id}/view`}>
|
||||
View invoice
|
||||
</Button>
|
||||
<Button external href={invoiceUrl(invoice.$id)}>View invoice</Button>
|
||||
|
||||
<PaymentBoxes
|
||||
bind:paymentMethod
|
||||
|
||||
+4
-1
@@ -1,4 +1,5 @@
|
||||
import { getApiEndpoint, sdk } from '$lib/stores/sdk';
|
||||
import { createImpersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
@@ -12,6 +13,8 @@ export const load: PageLoad = async ({ params }) => {
|
||||
|
||||
return redirect(
|
||||
302,
|
||||
`${endpoint}/organizations/${params.organization}/invoices/${invoice.$id}/download`
|
||||
createImpersonatedResourceUrl(
|
||||
`${endpoint}/organizations/${params.organization}/invoices/${invoice.$id}/download`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getApiEndpoint, sdk } from '$lib/stores/sdk';
|
||||
import { createImpersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
@@ -13,6 +14,8 @@ export const load: PageLoad = async ({ params }) => {
|
||||
|
||||
return redirect(
|
||||
302,
|
||||
`${endpoint}/organizations/${params.organization}/invoices/${invoice.$id}/view`
|
||||
createImpersonatedResourceUrl(
|
||||
`${endpoint}/organizations/${params.organization}/invoices/${invoice.$id}/view`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { formatCurrency } from '$lib/helpers/numbers';
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
import { ActionMenu, Badge, Icon, Link, Popover, Table } from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
@@ -24,6 +25,12 @@
|
||||
$selectedInvoice = invoice;
|
||||
$showRetryModal = true;
|
||||
}
|
||||
|
||||
function invoiceUrl(invoiceId: string, action: 'view' | 'download') {
|
||||
return $impersonatedResourceUrl(
|
||||
`${endpoint}/organizations/${page.params.organization}/invoices/${invoiceId}/${action}`
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Table.Root
|
||||
@@ -106,12 +113,12 @@
|
||||
<ActionMenu.Item.Anchor
|
||||
trailingIcon={IconExternalLink}
|
||||
external
|
||||
href={`${endpoint}/organizations/${page.params.organization}/invoices/${invoice.$id}/view`}>
|
||||
href={invoiceUrl(invoice.$id, 'view')}>
|
||||
View invoice
|
||||
</ActionMenu.Item.Anchor>
|
||||
<ActionMenu.Item.Anchor
|
||||
trailingIcon={IconDownload}
|
||||
href={`${endpoint}/organizations/${page.params.organization}/invoices/${invoice.$id}/download`}>
|
||||
href={invoiceUrl(invoice.$id, 'download')}>
|
||||
Download PDF
|
||||
</ActionMenu.Item.Anchor>
|
||||
{#if status === 'overdue' || status === 'failed'}
|
||||
|
||||
+7
-4
@@ -3,6 +3,7 @@ import { DeploymentDownloadType, type Models } from '@appwrite.io/console';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { page } from '$app/stores';
|
||||
import { createImpersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
|
||||
export const func = derived(page, ($page) => $page.data.function as Models.Function);
|
||||
export const deploymentList = derived(
|
||||
@@ -150,22 +151,24 @@ export const columns = writable<Column[]>([
|
||||
|
||||
export function getOutputDownload(funcId: string, deploymentId: string) {
|
||||
const p = get(page);
|
||||
return (
|
||||
return createImpersonatedResourceUrl(
|
||||
sdk.forProject(p.params.region, p.params.project).functions.getDeploymentDownload({
|
||||
functionId: funcId,
|
||||
deploymentId,
|
||||
type: DeploymentDownloadType.Output
|
||||
}) + '&mode=admin'
|
||||
}),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
export function getSourceDownload(funcId: string, deploymentId: string) {
|
||||
const p = get(page);
|
||||
|
||||
return (
|
||||
return createImpersonatedResourceUrl(
|
||||
sdk.forProject(p.params.region, p.params.project).functions.getDeploymentDownload({
|
||||
functionId: funcId,
|
||||
deploymentId,
|
||||
type: DeploymentDownloadType.Source
|
||||
}) + '&mode=admin'
|
||||
}),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
|
||||
+7
-4
@@ -17,6 +17,7 @@
|
||||
import { ActionMenu, Icon, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
|
||||
export let selectedDeployment: Models.Deployment;
|
||||
export let deployment: Models.Deployment;
|
||||
@@ -28,21 +29,23 @@
|
||||
export let inCard = false;
|
||||
|
||||
function getOutputDownload(deploymentId: string) {
|
||||
return (
|
||||
return $impersonatedResourceUrl(
|
||||
sdk.forProject(page.params.region, page.params.project).sites.getDeploymentDownload({
|
||||
siteId: page.params.site,
|
||||
deploymentId: deploymentId,
|
||||
type: DeploymentDownloadType.Output
|
||||
}) + '&mode=admin'
|
||||
}),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
function getSourceDownload(deploymentId: string) {
|
||||
return (
|
||||
return $impersonatedResourceUrl(
|
||||
sdk.forProject(page.params.region, page.params.project).sites.getDeploymentDownload({
|
||||
siteId: page.params.site,
|
||||
deploymentId: deploymentId,
|
||||
type: DeploymentDownloadType.Source
|
||||
}) + '&mode=admin'
|
||||
}),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { regionalProtocol } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
|
||||
let {
|
||||
site,
|
||||
@@ -71,13 +72,15 @@
|
||||
}
|
||||
|
||||
function getFilePreview(fileId: string) {
|
||||
return sdk.forConsoleIn(page.params.region).storage.getFilePreview({
|
||||
bucketId: 'screenshots',
|
||||
fileId,
|
||||
width: 1024,
|
||||
height: 576,
|
||||
output: ImageFormat.Avif
|
||||
});
|
||||
return $impersonatedResourceUrl(
|
||||
sdk.forConsoleIn(page.params.region).storage.getFilePreview({
|
||||
bucketId: 'screenshots',
|
||||
fileId,
|
||||
width: 1024,
|
||||
height: 576,
|
||||
output: ImageFormat.Avif
|
||||
})
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import { IconExclamation } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Link } from '$lib/elements';
|
||||
import { getFrameworkIcon } from '$lib/stores/sites';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
|
||||
export let siteList: Models.SiteList;
|
||||
|
||||
@@ -33,13 +34,15 @@
|
||||
}
|
||||
|
||||
function getFilePreview(fileId: string) {
|
||||
return sdk.forConsoleIn(page.params.region).storage.getFilePreview({
|
||||
bucketId: 'screenshots',
|
||||
fileId,
|
||||
width: 1024,
|
||||
height: 576,
|
||||
output: ImageFormat.Avif
|
||||
});
|
||||
return $impersonatedResourceUrl(
|
||||
sdk.forConsoleIn(page.params.region).storage.getFilePreview({
|
||||
bucketId: 'screenshots',
|
||||
fileId,
|
||||
width: 1024,
|
||||
height: 576,
|
||||
output: ImageFormat.Avif
|
||||
})
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
+10
-11
@@ -34,6 +34,7 @@
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
import type { PageProps } from './$types';
|
||||
|
||||
const { data }: PageProps = $props();
|
||||
@@ -44,17 +45,15 @@
|
||||
let isDragging = $state(false);
|
||||
|
||||
function getPreview(fileId: string) {
|
||||
return (
|
||||
sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.storage.getFilePreview({
|
||||
bucketId: page.params.bucket,
|
||||
fileId,
|
||||
height: 128,
|
||||
width: 128,
|
||||
output: ImageFormat.Avif
|
||||
})
|
||||
.toString() + '&mode=admin'
|
||||
return $impersonatedResourceUrl(
|
||||
sdk.forProject(page.params.region, page.params.project).storage.getFilePreview({
|
||||
bucketId: page.params.bucket,
|
||||
fileId,
|
||||
height: 128,
|
||||
width: 128,
|
||||
output: ImageFormat.Avif
|
||||
}),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+20
-13
@@ -38,6 +38,7 @@
|
||||
import { ImageFormat, type Models } from '@appwrite.io/console';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { Menu } from '$lib/components/menu';
|
||||
import { impersonatedResourceUrl } from '$lib/appwrite/impersonation';
|
||||
|
||||
let showFileAlert = true;
|
||||
|
||||
@@ -50,33 +51,39 @@
|
||||
let showCopyUrlModal = false;
|
||||
let selectedFileToken: Models.ResourceToken | null = null;
|
||||
|
||||
const getPreview = (fileId: string) =>
|
||||
sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.storage.getFilePreview({
|
||||
function getPreview(fileId: string) {
|
||||
return $impersonatedResourceUrl(
|
||||
sdk.forProject(page.params.region, page.params.project).storage.getFilePreview({
|
||||
bucketId: $file.bucketId,
|
||||
fileId,
|
||||
width: 640,
|
||||
height: 300,
|
||||
output: ImageFormat.Avif
|
||||
})
|
||||
.toString() + '&mode=admin';
|
||||
const getView = (fileId: string) =>
|
||||
sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.storage.getFileView({ bucketId: $file.bucketId, fileId })
|
||||
.toString() + '&mode=admin';
|
||||
}),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
|
||||
function getView(fileId: string) {
|
||||
return $impersonatedResourceUrl(
|
||||
sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.storage.getFileView({ bucketId: $file.bucketId, fileId }),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
|
||||
$: if (filePermissions) {
|
||||
arePermsDisabled = !symmetricDifference(filePermissions, $file.$permissions).length;
|
||||
}
|
||||
|
||||
function downloadFile() {
|
||||
return (
|
||||
return $impersonatedResourceUrl(
|
||||
sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.storage.getFileDownload({ bucketId: $file.bucketId, fileId: $file.$id })
|
||||
.toString() + '&mode=admin'
|
||||
.toString(),
|
||||
{ mode: 'admin' }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user