Skip to content

Commit 9275765

Browse files
committed
Merge remote-tracking branch 'origin/main' into chore/usage-update
2 parents c7aee64 + 692c13d commit 9275765

10 files changed

Lines changed: 7108 additions & 93 deletions

File tree

pnpm-lock.yaml

Lines changed: 7021 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/constants.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,15 @@ export const defaultRoles: string[] = ['owner'];
155155

156156
// these are kept for backwards compatibility with keys and events that already exists.
157157
// for the new ones, we use the new terminology.
158-
export const scopes: {
158+
export type ScopeDefinition = {
159159
scope: string;
160160
description: string;
161161
category: string;
162162
icon: string;
163-
}[] = [
163+
deprecated?: boolean;
164+
};
165+
166+
export const scopes: ScopeDefinition[] = [
164167
{
165168
scope: 'sessions.write',
166169
description: "Access to create, update and delete your project's sessions",
@@ -207,13 +210,15 @@ export const scopes: {
207210
scope: 'collections.read',
208211
description: "Access to read your project's database collections",
209212
category: 'Database',
210-
icon: 'database'
213+
icon: 'database',
214+
deprecated: true
211215
},
212216
{
213217
scope: 'collections.write',
214218
description: "Access to create, update, and delete your project's database collections",
215219
category: 'Database',
216-
icon: 'database'
220+
icon: 'database',
221+
deprecated: true
217222
},
218223
{
219224
scope: 'tables.read',
@@ -231,14 +236,16 @@ export const scopes: {
231236
scope: 'attributes.read',
232237
description: "Access to read your project's database collection's attributes",
233238
category: 'Database',
234-
icon: 'database'
239+
icon: 'database',
240+
deprecated: true
235241
},
236242
{
237243
scope: 'attributes.write',
238244
description:
239245
"Access to create, update, and delete your project's database collection's attributes",
240246
category: 'Database',
241-
icon: 'database'
247+
icon: 'database',
248+
deprecated: true
242249
},
243250
{
244251
scope: 'columns.read',
@@ -268,13 +275,15 @@ export const scopes: {
268275
scope: 'documents.read',
269276
description: "Access to read your project's database documents",
270277
category: 'Database',
271-
icon: 'database'
278+
icon: 'database',
279+
deprecated: true
272280
},
273281
{
274282
scope: 'documents.write',
275283
description: "Access to create, update, and delete your project's database documents",
276284
category: 'Database',
277-
icon: 'database'
285+
icon: 'database',
286+
deprecated: true
278287
},
279288
{
280289
scope: 'rows.read',
@@ -466,7 +475,7 @@ export const scopes: {
466475
}
467476
];
468477

469-
export const cloudOnlyBackupScopes = [
478+
export const cloudOnlyBackupScopes: ScopeDefinition[] = [
470479
{
471480
scope: 'policies.read',
472481
description: 'Access to read your database backup policies',

src/lib/helpers/fingerprint.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,29 @@ import { env } from '$env/dynamic/public';
33
const SECRET = env.PUBLIC_CONSOLE_FINGERPRINT_KEY ?? '';
44
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
55

6+
/** Cached server timestamp and the local time it was fetched at, for interpolation. */
7+
let serverTimeCache: { serverSecs: number; fetchedAtMs: number } | null = null;
8+
9+
/**
10+
* Cache the server's clock so fingerprint timestamps always align with the
11+
* backend's clock, regardless of local clock drift.
12+
*
13+
* @param serverTimeSecs - the server's unix timestamp in seconds
14+
* (e.g. parsed from a response Date header)
15+
*/
16+
export function syncServerTime(serverTimeSecs: number): void {
17+
if (serverTimeCache) return;
18+
serverTimeCache = { serverSecs: serverTimeSecs, fetchedAtMs: Date.now() };
19+
}
20+
21+
function getServerTimestamp(): number {
22+
if (!serverTimeCache) {
23+
return Math.floor(Date.now() / 1000);
24+
}
25+
const elapsedSecs = Math.floor((Date.now() - serverTimeCache.fetchedAtMs) / 1000);
26+
return serverTimeCache.serverSecs + elapsedSecs;
27+
}
28+
629
async function sha256(message: string): Promise<string> {
730
if (!crypto?.subtle) {
831
console.warn('crypto.subtle unavailable, fingerprinting disabled');
@@ -204,7 +227,7 @@ export async function generateFingerprintToken(): Promise<string> {
204227

205228
const signals: BrowserSignals = {
206229
...staticSignals,
207-
timestamp: Math.floor(Date.now() / 1000)
230+
timestamp: getServerTimestamp()
208231
};
209232

210233
const payload = JSON.stringify(signals);

src/lib/layout/navigation.svelte

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
<script lang="ts">
2-
import { page } from '$app/state';
32
import { getContext } from 'svelte';
43
import type { Writable } from 'svelte/store';
54
65
export let subNavigation;
7-
8-
$: subNavigation = page.data.subNavigation;
96
// We need to have this second variable, because we only want narrow
107
// to change automatically if we change from having a second side nav to
118
// not having one, not when the second side nav changes to a different value.

src/lib/layout/shell.svelte

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import { hasOnboardingDismissed } from '$lib/helpers/onboarding';
1616
import { isSidebarOpen, noWidthTransition } from '$lib/stores/sidebar';
1717
import { page } from '$app/state';
18+
import { page as pageStore } from '$app/stores';
1819
import { BillingPlanGroup, type Models } from '@appwrite.io/console';
1920
import { getSidebarState, isInDatabasesRoute, updateSidebarState } from '$lib/helpers/sidebar';
2021
import { isTabletViewport } from '$lib/stores/viewport';
@@ -191,7 +192,7 @@
191192
192193
$: state = $isSidebarOpen ? 'open' : 'closed';
193194
194-
$: subNavigation = page.data.subNavigation;
195+
$: subNavigation = $pageStore.data.subNavigation;
195196
196197
$: shouldRenderSidebar =
197198
!$isNewWizardStatusOpen && showSideNavigation && !$showOnboardingAnimation;
@@ -235,14 +236,14 @@
235236
project={activeProject}
236237
progressCard={getProgressCard()}
237238
avatar={navbarProps.avatar}
238-
bind:subNavigation
239+
{subNavigation}
239240
bind:sideBarIsOpen={$isSidebarOpen}
240241
bind:showAccountMenu
241242
bind:state />
242243
{/if}
243244

244245
{#if !$showOnboardingAnimation}
245-
<SideNavigation bind:subNavigation />
246+
<SideNavigation {subNavigation} />
246247
{/if}
247248
</div>
248249

src/lib/stores/sdk.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ export function getApiEndpoint(region?: string): string {
5454
const hostname = url.host; // "hostname:port" (or just "hostname" if no port)
5555

5656
// If instance supports multi-region, add the region subdomain.
57-
const subdomain = isMultiRegionSupported(url) ? getSubdomain(region) : '';
57+
let subdomain = isMultiRegionSupported(url) ? getSubdomain(region) : '';
58+
if (subdomain && hostname.startsWith(subdomain)) {
59+
subdomain = '';
60+
}
5861

5962
return `${protocol}//${subdomain}${hostname}/v1`;
6063
}

src/routes/(console)/+layout.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Platform, Query } from '@appwrite.io/console';
66
import { makePlansMap } from '$lib/helpers/billing';
77
import { plansInfo as plansInfoStore } from '$lib/stores/billing';
88
import { normalizeConsoleVariables } from '$lib/helpers/domains';
9+
import { syncServerTime } from '$lib/helpers/fingerprint';
910

1011
export const load: LayoutLoad = async ({ depends, parent }) => {
1112
const { organizations, plansInfo } = await parent();
@@ -28,7 +29,14 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
2829
plansArrayPromise,
2930
fetch(`${endpoint}/health/version`, {
3031
headers: { 'X-Appwrite-Project': project as string }
31-
}).then((response) => response.json() as { version?: string }),
32+
}).then((response) => {
33+
const dateHeader = response.headers.get('Date');
34+
const parsed = dateHeader ? new Date(dateHeader).getTime() : NaN;
35+
if (Number.isFinite(parsed)) {
36+
syncServerTime(Math.floor(parsed / 1000));
37+
}
38+
return response.json() as { version?: string };
39+
}),
3240
sdk.forConsole.console.variables()
3341
]);
3442

src/routes/(console)/project-[region]-[project]/overview/(components)/keyDetails.svelte

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import { symmetricDifference } from '$lib/helpers/array';
1717
import Scopes from '../api-keys/scopes.svelte';
1818
import { InteractiveText, Layout, Typography } from '@appwrite.io/pink-svelte';
19-
import { getEffectiveScopes } from '../api-keys/scopes.svelte';
2019
2120
export let key: Models.DevKey | Models.Key;
2221
export let keyType: 'api' | 'dev' = 'api';
@@ -163,8 +162,6 @@
163162
{#if isApiKey}
164163
<Form onSubmit={updateScopes}>
165164
{@const apiKey = asApiKey(key)}
166-
{@const apiKeyCorrectScopes = getEffectiveScopes(apiKey.scopes)}
167-
{@const currentEffective = scopes ? getEffectiveScopes(scopes) : null}
168165
<CardGrid>
169166
<svelte:fragment slot="title">Scopes</svelte:fragment>
170167
You can choose which permission scope to grant your application. It is a best practice
@@ -178,8 +175,7 @@
178175
<svelte:fragment slot="actions">
179176
<Button
180177
submit
181-
disabled={scopes &&
182-
!symmetricDifference(currentEffective, apiKeyCorrectScopes).length}
178+
disabled={scopes && !symmetricDifference(scopes, apiKey.scopes).length}
183179
>Update</Button>
184180
</svelte:fragment>
185181
</CardGrid>

src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import { Badge, Layout, Table } from '@appwrite.io/pink-svelte';
1212
import DeleteBatch from './deleteBatch.svelte';
1313
import { capitalize } from '$lib/helpers/string';
14-
import { getEffectiveScopes } from '../api-keys/scopes.svelte';
1514
1615
let {
1716
keyType = 'api',
@@ -31,7 +30,7 @@
3130
3231
function getApiKeyScopeCount(key: Models.Key | Models.DevKey) {
3332
const apiKey = key as Models.Key;
34-
return getEffectiveScopes(apiKey.scopes).length;
33+
return apiKey.scopes.length;
3534
}
3635
3736
function getExpiryDetails(key: Models.Key | Models.DevKey): {

0 commit comments

Comments
 (0)