Skip to content

Commit 60d0bdb

Browse files
authored
Merge pull request #2519 from appwrite/fix-realtime-logic
2 parents 52dc725 + fff134e commit 60d0bdb

24 files changed

Lines changed: 274 additions & 279 deletions

File tree

src/lib/components/backupRestoreBox.svelte

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,18 @@
125125
126126
onMount(() => {
127127
// fast path: don't subscribe if org is on a free plan or is self-hosted.
128-
if (isSelfHosted || (isCloud && $organization.billingPlan === BillingPlan.FREE)) return;
129-
130-
return realtime
131-
.forProject(page.params.region, page.params.project)
132-
.subscribe('console', (response) => {
133-
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
134-
135-
if (
136-
response.events.includes('archives.*') ||
137-
response.events.includes('restorations.*')
138-
) {
139-
updateOrAddItem(response.payload);
140-
}
141-
});
128+
if (isSelfHosted || (isCloud && $organization?.billingPlan === BillingPlan.FREE)) return;
129+
130+
return realtime.forProject(page.params.region, 'console', (response) => {
131+
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
132+
133+
if (
134+
response.events.includes('archives.*') ||
135+
response.events.includes('restorations.*')
136+
) {
137+
updateOrAddItem(response.payload);
138+
}
139+
});
142140
});
143141
</script>
144142

src/lib/components/csvImportBox.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import { onMount } from 'svelte';
33
import { base } from '$app/paths';
44
import { page } from '$app/state';
5-
import { sdk } from '$lib/stores/sdk';
65
import { Dependencies } from '$lib/constants';
6+
import { realtime, sdk } from '$lib/stores/sdk';
77
import { goto, invalidate } from '$app/navigation';
88
import { getProjectId } from '$lib/helpers/project';
99
import { addNotification } from '$lib/stores/notifications';
@@ -187,7 +187,7 @@
187187
migrations.migrations.forEach(updateOrAddItem);
188188
});
189189
190-
return sdk.forConsoleIn(page.params.region).realtime.subscribe('console', (response) => {
190+
return realtime.forConsole(page.params.region, 'console', (response) => {
191191
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
192192
if (response.events.includes('migrations.*')) {
193193
updateOrAddItem(response.payload as Payload);

src/lib/components/migrationBox.svelte

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,14 @@
5050
})();
5151
5252
onMount(() => {
53-
return realtime
54-
.forProject(page.params.region, page.params.project)
55-
.subscribe<Models.Migration>(['console'], async (response) => {
56-
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
57-
if (response.events.includes('migrations.*')) {
58-
if (response.payload.source === 'Backup') return;
59-
migration = response.payload;
60-
}
61-
});
53+
return realtime.forProject(page.params.region, ['console'], async (response) => {
54+
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
55+
if (response.events.includes('migrations.*')) {
56+
const payload = response.payload as Models.Migration;
57+
if (payload.source === 'Backup') return;
58+
migration = payload;
59+
}
60+
});
6261
});
6362
</script>
6463

src/lib/stores/sdk.ts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import {
4242
SUBDOMAIN_TOR
4343
} from '$lib/constants';
4444
import { building } from '$app/environment';
45-
import { getProjectId } from '$lib/helpers/project';
4645

4746
export function getApiEndpoint(region?: string): string {
4847
if (building) return '';
@@ -141,12 +140,32 @@ const sdkForProject = {
141140
};
142141

143142
export const realtime = {
144-
forProject(region: string, _projectId: string) {
143+
forProject(
144+
region: string,
145+
channels: string | string[],
146+
callback: AppwriteRealtimeResponseEvent
147+
) {
145148
const endpoint = getApiEndpoint(region);
146149
if (endpoint !== clientRealtime.config.endpoint) {
147150
clientRealtime.setEndpoint(endpoint);
148151
}
149-
return clientRealtime;
152+
153+
// because uses a different client!
154+
const realtime = new Realtime(clientRealtime);
155+
156+
return createRealtimeSubscription(realtime, channels, callback);
157+
},
158+
159+
forConsole(
160+
region: string,
161+
channels: string | string[],
162+
callback: AppwriteRealtimeResponseEvent
163+
): () => void {
164+
const realtimeInstance = region
165+
? sdk.forConsoleIn(region).realtime
166+
: sdk.forConsole.realtime;
167+
168+
return createRealtimeSubscription(realtimeInstance, channels, callback);
150169
}
151170
};
152171

@@ -176,8 +195,8 @@ export const sdk = {
176195
};
177196

178197
export enum RuleType {
179-
DEPLOYMENT = 'deployment',
180198
API = 'api',
199+
DEPLOYMENT = 'deployment',
181200
REDIRECT = 'redirect'
182201
}
183202

@@ -191,11 +210,24 @@ export enum RuleTrigger {
191210
MANUAL = 'manual'
192211
}
193212

194-
/**
195-
* Some type imports are broken on the SDK, this works correctly for the time being!
196-
*/
197-
export type AppwriteRealtimeSubscription = Awaited<ReturnType<Realtime['subscribe']>>;
198-
199-
export const createAdminClient = () => {
200-
return new Client().setEndpoint(getApiEndpoint()).setMode('admin').setProject(getProjectId());
213+
export type RealtimeResponse = {
214+
events: string[];
215+
channels: string[];
216+
timestamp: string;
217+
payload: unknown;
201218
};
219+
220+
export type AppwriteRealtimeResponseEvent = (response: RealtimeResponse) => void;
221+
222+
function createRealtimeSubscription(
223+
realtimeInstance: Realtime,
224+
channels: string | string[],
225+
callback: AppwriteRealtimeResponseEvent
226+
): () => void {
227+
const channelsArray = Array.isArray(channels) ? channels : [channels];
228+
const subscriptionPromise = realtimeInstance.subscribe(channelsArray, callback);
229+
230+
return () => {
231+
subscriptionPromise.then((sub) => sub.close());
232+
};
233+
}

src/routes/(console)/project-[region]-[project]/+layout.svelte

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,13 @@
2727
import CsvImportBox from '$lib/components/csvImportBox.svelte';
2828
2929
onMount(() => {
30-
return realtime
31-
.forProject(page.params.region, page.params.project)
32-
.subscribe(['project', 'console'], (response) => {
33-
if (response.events.includes('stats.connections')) {
34-
for (const [projectId, value] of Object.entries(response.payload)) {
35-
stats.add(projectId, [new Date(response.timestamp).toISOString(), value]);
36-
}
30+
return realtime.forProject(page.params.region, ['project', 'console'], (response) => {
31+
if (response.events.includes('stats.connections')) {
32+
for (const [projectId, value] of Object.entries(response.payload)) {
33+
stats.add(projectId, [new Date(response.timestamp).toISOString(), value]);
3734
}
38-
});
35+
}
36+
});
3937
});
4038
4139
$: $registerCommands([

src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/+page.svelte

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,14 @@
161161
};
162162
163163
onMount(() => {
164-
return realtime
165-
.forProject(page.params.region, page.params.project)
166-
.subscribe(['project', 'console'], (response) => {
167-
// fast path return.
168-
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
169-
170-
if (
171-
response.events.includes('archives.*') ||
172-
response.events.includes('policies.*')
173-
) {
174-
invalidate(Dependencies.BACKUPS);
175-
}
176-
});
164+
return realtime.forProject(page.params.region, ['project', 'console'], (response) => {
165+
// fast path return.
166+
if (!response.channels.includes(`projects.${getProjectId()}`)) return;
167+
168+
if (response.events.includes('archives.*') || response.events.includes('policies.*')) {
169+
invalidate(Dependencies.BACKUPS);
170+
}
171+
});
177172
});
178173
</script>
179174

src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<script lang="ts">
2121
import { goto, invalidate } from '$app/navigation';
2222
import { Dependencies } from '$lib/constants';
23-
import { realtime, sdk } from '$lib/stores/sdk';
23+
import { type RealtimeResponse, realtime, sdk } from '$lib/stores/sdk';
2424
import { onMount } from 'svelte';
2525
import {
2626
table,
@@ -66,7 +66,6 @@
6666
6767
import IndexesSuggestions from '../(suggestions)/indexes.svelte';
6868
import { showIndexesSuggestions, tableColumnSuggestions } from '../(suggestions)';
69-
import type { RealtimeResponseEvent } from '@appwrite.io/console';
7069
7170
let editRow: EditRow;
7271
let editRelatedRow: EditRelatedRow;
@@ -83,35 +82,33 @@
8382
*/
8483
let isWaterfallFromFaker = false;
8584
86-
let columnCreationHandler: ((response: RealtimeResponseEvent<unknown>) => void) | null = null;
85+
let columnCreationHandler: ((response: RealtimeResponse) => void) | null = null;
8786
8887
onMount(() => {
8988
expandTabs.set(preferences.getKey('tableHeaderExpanded', true));
9089
91-
return realtime
92-
.forProject(page.params.region, page.params.project)
93-
.subscribe(['project', 'console'], (response) => {
90+
return realtime.forProject(page.params.region, ['project', 'console'], (response) => {
91+
if (
92+
response.events.includes('databases.*.tables.*.columns.*') ||
93+
response.events.includes('databases.*.tables.*.indexes.*')
94+
) {
95+
if (isWaterfallFromFaker) {
96+
columnCreationHandler?.(response);
97+
}
98+
99+
// don't invalidate when -
100+
// 1. from faker
101+
// 2. ai columns creation
102+
// 3. ai indexes creation
94103
if (
95-
response.events.includes('databases.*.tables.*.columns.*') ||
96-
response.events.includes('databases.*.tables.*.indexes.*')
104+
!isWaterfallFromFaker &&
105+
!$showIndexesSuggestions &&
106+
!$tableColumnSuggestions.table
97107
) {
98-
if (isWaterfallFromFaker) {
99-
columnCreationHandler?.(response);
100-
}
101-
102-
// don't invalidate when -
103-
// 1. from faker
104-
// 2. ai columns creation
105-
// 3. ai indexes creation
106-
if (
107-
!isWaterfallFromFaker &&
108-
!$showIndexesSuggestions &&
109-
!$tableColumnSuggestions.table
110-
) {
111-
invalidate(Dependencies.TABLE);
112-
}
108+
invalidate(Dependencies.TABLE);
113109
}
114-
});
110+
}
111+
});
115112
});
116113
117114
// TODO: use route ids instead of pathname
@@ -268,7 +265,7 @@
268265
const availableColumns = new Set<string>();
269266
const waitPromise = new Promise<void>((resolve) => (resolvePromise = resolve));
270267
271-
columnCreationHandler = (response) => {
268+
columnCreationHandler = (response: RealtimeResponse) => {
272269
const { events, payload } = response;
273270
274271
if (

src/routes/(console)/project-[region]-[project]/functions/function-[function]/+layout.svelte

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,29 @@
2121
2222
onMount(() => {
2323
let previousStatus = null;
24-
return realtime
25-
.forProject(page.params.region, page.params.project)
26-
.subscribe<Models.Deployment>('console', (message) => {
27-
if (
28-
message.payload.status !== 'ready' &&
29-
previousStatus === message.payload.status
30-
) {
31-
return;
32-
}
33-
previousStatus = message.payload.status;
34-
if (message.events.includes('functions.*.deployments.*.create')) {
35-
invalidate(Dependencies.DEPLOYMENTS);
36-
return;
37-
}
38-
if (message.events.includes('functions.*.deployments.*.update')) {
39-
invalidate(Dependencies.DEPLOYMENTS);
40-
invalidate(Dependencies.FUNCTION);
41-
return;
42-
}
43-
if (message.events.includes('functions.*.deployments.*.delete')) {
44-
invalidate(Dependencies.DEPLOYMENTS);
45-
return;
46-
}
47-
});
24+
return realtime.forProject(page.params.region, 'console', (response) => {
25+
const payload = response.payload as Models.Deployment;
26+
if (payload.status !== 'ready' && previousStatus === payload.status) {
27+
return;
28+
}
29+
30+
previousStatus = payload.status;
31+
if (response.events.includes('functions.*.deployments.*.create')) {
32+
invalidate(Dependencies.DEPLOYMENTS);
33+
return;
34+
}
35+
36+
if (response.events.includes('functions.*.deployments.*.update')) {
37+
invalidate(Dependencies.DEPLOYMENTS);
38+
invalidate(Dependencies.FUNCTION);
39+
return;
40+
}
41+
42+
if (response.events.includes('functions.*.deployments.*.delete')) {
43+
invalidate(Dependencies.DEPLOYMENTS);
44+
return;
45+
}
46+
});
4847
});
4948
5049
$: $registerCommands([

src/routes/(console)/project-[region]-[project]/functions/function-[function]/deployment-[deployment]/+page.svelte

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { Button } from '$lib/elements/forms';
33
import { Container } from '$lib/layout';
4-
import { sdk } from '$lib/stores/sdk';
4+
import { realtime } from '$lib/stores/sdk';
55
import { onMount } from 'svelte';
66
import { type Models } from '@appwrite.io/console';
77
import { page } from '$app/state';
@@ -44,24 +44,18 @@
4444
let showRedeploy = false;
4545
4646
onMount(() => {
47-
const unsubscribe = sdk.forConsole.client.subscribe<Models.Deployment>(
48-
'console',
49-
(message) => {
50-
if (
51-
message.events.includes(
52-
`functions.${page.params.function}.deployments.${page.params.deployment}.update`
53-
)
54-
) {
55-
if (message.payload.status === 'ready') {
56-
invalidate(Dependencies.DEPLOYMENT);
57-
}
47+
return realtime.forProject(page.params.region, 'console', (response) => {
48+
if (
49+
response.events.includes(
50+
`functions.${page.params.function}.deployments.${page.params.deployment}.update`
51+
)
52+
) {
53+
const payload = response.payload as Models.Deployment;
54+
if (payload.status === 'ready') {
55+
invalidate(Dependencies.DEPLOYMENT);
5856
}
5957
}
60-
);
61-
62-
return () => {
63-
unsubscribe();
64-
};
58+
});
6559
});
6660
6761
export function badgeTypeDeployment(status: string) {

0 commit comments

Comments
 (0)