Я хочу использовать аутентификацию Supabase для своего приложения и просто создал эту службу аутентификации:
import { Injectable } from '@angular/core';
import { AuthResponse, createClient } from '@supabase/supabase-js';
import { environment } from '../../environments/environment';
import { from, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
supabase = createClient(environment.supabaseUrl, environment.supabaseKey);
register(email: string, password: string): Observable<AuthResponse> {
const promise = this.supabase.auth.signUp({
email,
password
})
return from(promise);
}
login(email: string, password: string): Observable<AuthResponse> {
const promise = this.supabase.auth.signInWithPassword({
email,
password
});
return from(promise);
}
}
в компоненте входа я вызываю метод входа из службы аутентификации:
import { Component, inject } from '@angular/core';
import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../auth/auth.service';
@Component({
selector: 'app-login',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './login.component.html',
styleUrl: './login.component.css'
})
export class LoginComponent {
authService = inject(AuthService);
router = inject(Router);
errorMessage: string | null = null;
contactForm = new FormGroup({
email: new FormControl(''),
password: new FormControl(''),
});
handleSubmit() {
const rawForm = this.contactForm.getRawValue();
if (rawForm.email && rawForm.password) {
this.authService
.login(rawForm.email, rawForm.password)
.subscribe((result) => {
if (result.error) {
this.errorMessage = result.error.message;
} else {
this.router.navigateByUrl('/');
}
});
}
}
}
Я также включил withFetch() для HttpClient, чтобы улучшить возможный SSR:
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideClientHydration(),
provideHttpClient(withFetch()),
importProvidersFrom(HttpClientModule)]
};
но когда я вызываю http://localhost:4200/login, я получаю эту ошибку:
[vite] Internal server error: Page /login did not render in 30 seconds.
at Timeout.<anonymous> (C:\Users\kursp\Desktop\deluxe-detail\node_modules\@angular\build\src\utils\server-rendering\render-page.js:90:90)
at Timeout.timer (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:2249:37)
at _ZoneDelegate.invokeTask (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:398:33)
at _ZoneImpl.runTask (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:158:47)
at invokeTask (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:479:34)
at Timeout.ZoneTask.invoke (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:468:48)
at Timeout.data.args.<computed> (C:\Users\kursp\Desktop\deluxe-detail\node_modules\zone.js\fesm2015\zone-node.js:2231:32)
at listOnTimeout (node:internal/timers:573:17)
at process.processTimers (node:internal/timers:514:7)
Я попробовал и добавил в конфигурацию опцию withFetch() для провайдеров. Я думал, что это может иметь какое-то влияние на SSR, хотя я использую супабазу, чтобы не нуждаться в явном бэкэнде для аутентификации.
Фактический результат: Тем не менее, ошибка тайм-аута через 30 секунд, я предполагаю, что у Zone.js есть некоторые задачи/процедуры для завершения соединения, но он никогда не достигает их фактического выполнения --> застревает на этом
Вероятно, метод инициализации createSupabase
создает постоянное соединение с данным environment.supabaseUrl
сервером, учитывая использование realtime-js под капотом (это упоминается в документации по инициализации)
Когда Angular инициализирует приложение, он создаст файл AuthService
. В рамках этого он вызовет метод createSupabase
для создания атрибута supabase
в AuthService
. Он выполнит эту инициализацию и создаст постоянное соединение.
Но эта связь никогда не разорвется. Если только для этого не будет вызван соответствующий API. Это означает, что Angular Zone.js будет постоянно иметь ожидающие выполнения задачи, связанные с открытым соединением, и поэтому никогда не будет считать приложение стабильным. А Angular требует, чтобы приложение было стабильным (без каких-либо ожидающих задач), чтобы его можно было считать инициализированным.
Чтобы решить эту проблему, вы можете запустить соединение после того, как Angular завершит инициализацию приложения. Для этого вы можете создать/инициализировать клиент Supabase, приложение стабильно, используя токен APP_INITIALIZER DI
Таким образом, приложение запустится как обычно. Затем, когда соединение станет стабильным, будет создано постоянное соединение, из-за которого приложение будет нестабильным. Но это нормально, когда все загрузилось. Имеет смысл, что приложение в этот момент нестабильно, поскольку данные постоянно поступают от клиента Supabase, пока вы не закроете его.
Например, это может быть обновленный код AuthService
:
@Injectable({
providedIn: 'root',
})
export class AuthService {
supabase!: ReturnType<typeof createClient>
constructor() {
inject(ApplicationRef)
.isStable.pipe(first((isStable) => isStable))
.subscribe(() => {
this.supabase = createClient(
environment.supabaseUrl,
environment.supabaseKey,
)
})
}
}
Дополнительную информацию об этой проблеме нестабильной инициализации можно найти в документации: https://angular.dev/errors/NG0506
Большое спасибо! Это помогло. Ты спас меня день или два.
Вы поделились полным кодом для
LoginComponent
, есть ли у вас код дляngOnInit
илиngAfterViewInit
?