feat(oauth): import favoris/abos Google (YouTube) et Twitch vers abonnements/likes
- Backend: server/oauth.mjs (auth URL, echange code, refresh, fetch
subscriptions/likes Google, follows Twitch), table oauth_connections
+ migration, routes /api/oauth/:provider/{url,callback,preview,import},
/api/oauth/{status,connections}
- Front: /library/import (connect, preview, import), service
oauth-import, lien depuis Abonnements
- Config: GOOGLE_CLIENT_ID/SECRET/REDIRECT_URI, TWITCH_REDIRECT_URI,
OAUTH_APP_BASE_URL documentes (.env.example, docker-compose),
passthrough docker-compose.local.yml, roadmap README cochee
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export interface OAuthConnection {
|
||||
provider: string;
|
||||
externalUserId: string | null;
|
||||
displayName: string | null;
|
||||
avatarUrl: string | null;
|
||||
scopes: string | null;
|
||||
expiresAt: number | null;
|
||||
connectedAt: number | null;
|
||||
updatedAt: number | null;
|
||||
}
|
||||
|
||||
export interface OAuthPreview {
|
||||
provider: string;
|
||||
subscriptions: Array<{ provider: string; externalId: string; title: string; handle?: string | null; avatarUrl?: string | null; url?: string | null }>;
|
||||
likes: Array<{ provider: string; videoId: string; title: string; thumbnail?: string }>;
|
||||
subscriptionCount: number;
|
||||
likeCount: number;
|
||||
}
|
||||
|
||||
export interface OAuthImportResult {
|
||||
provider: string;
|
||||
importedSubscriptions: number;
|
||||
importedLikes: number;
|
||||
skippedSubscriptions: number;
|
||||
skippedLikes: number;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class OAuthImportService {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
status(): Observable<{ google: { configured: boolean; missing: string[] }; twitch: { configured: boolean; missing: string[] } }> {
|
||||
return this.http.get<any>('/api/oauth/status');
|
||||
}
|
||||
|
||||
connections(): Observable<{ connections: OAuthConnection[]; status: any }> {
|
||||
return this.http.get<any>('/api/oauth/connections');
|
||||
}
|
||||
|
||||
async connect(provider: 'google' | 'twitch'): Promise<void> {
|
||||
const res: any = await this.http.get(`/api/oauth/${provider}/url`).toPromise().catch((e) => { throw e; });
|
||||
const url = res?.url;
|
||||
if (!url) throw new Error('oauth_url_failed');
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
disconnect(provider: string): Observable<void> {
|
||||
return this.http.delete<void>(`/api/oauth/${provider}`);
|
||||
}
|
||||
|
||||
preview(provider: string): Observable<OAuthPreview> {
|
||||
return this.http.get<OAuthPreview>(`/api/oauth/${provider}/preview`);
|
||||
}
|
||||
|
||||
importAll(provider: string, types: string[]): Observable<OAuthImportResult> {
|
||||
return this.http.post<OAuthImportResult>(`/api/oauth/${provider}/import`, { types });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user