feat: add identification page
This commit is contained in:
+58
-14
@@ -1,7 +1,24 @@
|
||||
import { Playlist, ServerType, ApiResponse, PlexServerConnection, PlexConnectionSettings, PlexLibrary, ReplacementRule, PathMappingConfig, PathMappingMode, PathMappingRules, SyncStrategy, ScheduleSettings, BackupSettings } from '../types';
|
||||
import { Playlist, ServerType, ApiResponse, PlexServerConnection, PlexConnectionSettings, PlexLibrary, ReplacementRule, PathMappingConfig, PathMappingMode, PathMappingRules, SyncStrategy, ScheduleSettings, BackupSettings, LoginCredentials, AuthResponse } from '../types';
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL || '';
|
||||
|
||||
const getAuthToken = (): string | null => {
|
||||
try {
|
||||
return localStorage.getItem('plexsync-token');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const authFetch = (input: RequestInfo | URL, init: RequestInit = {}) => {
|
||||
const token = getAuthToken();
|
||||
const headers = new Headers(init.headers || {});
|
||||
if (token) {
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
return fetch(input, { ...init, headers });
|
||||
};
|
||||
|
||||
const MODE_TO_STRATEGY: Record<string, SyncStrategy> = {
|
||||
local_force: SyncStrategy.LOCAL_OVERWRITE,
|
||||
remote_force: SyncStrategy.CLOUD_OVERWRITE,
|
||||
@@ -20,6 +37,9 @@ const handleResponse = async <T>(response: Response): Promise<ApiResponse<T>> =>
|
||||
try {
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
return { data: data as T, status: 'error', message: 'Unauthorized' };
|
||||
}
|
||||
return { data: data as T, status: 'error', message: (data as any)?.detail || response.statusText };
|
||||
}
|
||||
return { data, status: 'success' };
|
||||
@@ -97,8 +117,32 @@ const pathMappingToApi = (config: PathMappingConfig) => {
|
||||
};
|
||||
|
||||
export const apiService = {
|
||||
async getAuthConfig(): Promise<ApiResponse<{ enabled: boolean }>> {
|
||||
const response = await fetch(`${API_BASE}/api/auth/config`);
|
||||
return handleResponse(response);
|
||||
},
|
||||
|
||||
async login(creds: LoginCredentials): Promise<ApiResponse<AuthResponse>> {
|
||||
const response = await fetch(`${API_BASE}/api/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(creds),
|
||||
});
|
||||
return handleResponse(response);
|
||||
},
|
||||
|
||||
async me(): Promise<ApiResponse<{ username: string }>> {
|
||||
const response = await authFetch(`${API_BASE}/api/auth/me`);
|
||||
return handleResponse(response);
|
||||
},
|
||||
|
||||
async logout(): Promise<ApiResponse<{ status: string }>> {
|
||||
const response = await authFetch(`${API_BASE}/api/auth/logout`, { method: 'POST' });
|
||||
return handleResponse(response);
|
||||
},
|
||||
|
||||
async getSettings(): Promise<ApiResponse<{ strategy: SyncStrategy; pathMapping: PathMappingConfig; connection: PlexConnectionSettings; localPath: string }>> {
|
||||
const response = await fetch(`${API_BASE}/api/settings`);
|
||||
const response = await authFetch(`${API_BASE}/api/settings`);
|
||||
const result = await handleResponse<any>(response);
|
||||
if (result.status === 'success') {
|
||||
const mode = result.data.sync_mode as string;
|
||||
@@ -118,7 +162,7 @@ export const apiService = {
|
||||
|
||||
async updateSyncStrategy(strategy: SyncStrategy): Promise<ApiResponse<{ sync_mode: string }>> {
|
||||
const payload = { mode: STRATEGY_TO_MODE[strategy] };
|
||||
const response = await fetch(`${API_BASE}/api/settings/sync-mode`, {
|
||||
const response = await authFetch(`${API_BASE}/api/settings/sync-mode`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
@@ -128,7 +172,7 @@ export const apiService = {
|
||||
|
||||
async savePathMapping(config: PathMappingConfig): Promise<ApiResponse<null>> {
|
||||
const payload = pathMappingToApi(config);
|
||||
const response = await fetch(`${API_BASE}/api/settings/path-mapping`, {
|
||||
const response = await authFetch(`${API_BASE}/api/settings/path-mapping`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
@@ -137,7 +181,7 @@ export const apiService = {
|
||||
},
|
||||
|
||||
async updateLibrary(libraryName: string): Promise<ApiResponse<{ library_name: string }>> {
|
||||
const response = await fetch(`${API_BASE}/api/settings/library`, {
|
||||
const response = await authFetch(`${API_BASE}/api/settings/library`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ library_name: libraryName }),
|
||||
@@ -146,12 +190,12 @@ export const apiService = {
|
||||
},
|
||||
|
||||
async getScheduleSettings(): Promise<ApiResponse<ScheduleSettings & { nextRun?: string }>> {
|
||||
const response = await fetch(`${API_BASE}/api/schedule`);
|
||||
const response = await authFetch(`${API_BASE}/api/schedule`);
|
||||
return handleResponse(response);
|
||||
},
|
||||
|
||||
async saveScheduleSettings(settings: ScheduleSettings): Promise<ApiResponse<null>> {
|
||||
const response = await fetch(`${API_BASE}/api/schedule`, {
|
||||
const response = await authFetch(`${API_BASE}/api/schedule`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(settings),
|
||||
@@ -164,7 +208,7 @@ export const apiService = {
|
||||
if (serverType === ServerType.LOCAL && localPath) {
|
||||
params.append('local_path', localPath);
|
||||
}
|
||||
const response = await fetch(`${API_BASE}/api/playlists?${params.toString()}`, { signal });
|
||||
const response = await authFetch(`${API_BASE}/api/playlists?${params.toString()}`, { signal });
|
||||
const result = await handleResponse<any>(response);
|
||||
if (result.status === 'success' && (result.data as any)?.playlists) {
|
||||
return { data: (result.data.playlists as any[]).map(mapPlaylist), status: 'success' };
|
||||
@@ -173,7 +217,7 @@ export const apiService = {
|
||||
},
|
||||
|
||||
async getServerStatus(signal?: AbortSignal): Promise<ApiResponse<PlexServerConnection>> {
|
||||
const response = await fetch(`${API_BASE}/api/server`, { signal });
|
||||
const response = await authFetch(`${API_BASE}/api/server`, { signal });
|
||||
const result = await handleResponse<any>(response);
|
||||
if (result.status === 'success') {
|
||||
const info = result.data.serverInfo || {};
|
||||
@@ -194,7 +238,7 @@ export const apiService = {
|
||||
},
|
||||
|
||||
async connectToPlex(settings: PlexConnectionSettings, signal?: AbortSignal): Promise<ApiResponse<{ token: string; serverInfo: PlexServerConnection }>> {
|
||||
const response = await fetch(`${API_BASE}/api/connect`, {
|
||||
const response = await authFetch(`${API_BASE}/api/connect`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -219,7 +263,7 @@ export const apiService = {
|
||||
},
|
||||
|
||||
async syncPlaylists(strategy: SyncStrategy, _pathMapping: PathMappingConfig, localPath?: string): Promise<ApiResponse<null>> {
|
||||
const response = await fetch(`${API_BASE}/api/sync`, {
|
||||
const response = await authFetch(`${API_BASE}/api/sync`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -231,12 +275,12 @@ export const apiService = {
|
||||
},
|
||||
|
||||
async getSyncStatus(): Promise<ApiResponse<{ is_syncing: boolean; last_sync_time: string | null; status: string; error: string | null }>> {
|
||||
const response = await fetch(`${API_BASE}/api/sync/status`);
|
||||
const response = await authFetch(`${API_BASE}/api/sync/status`);
|
||||
return handleResponse(response);
|
||||
},
|
||||
|
||||
async getBackupSettings(): Promise<ApiResponse<BackupSettings>> {
|
||||
const response = await fetch(`${API_BASE}/api/backup/settings`);
|
||||
const response = await authFetch(`${API_BASE}/api/backup/settings`);
|
||||
const result = await handleResponse<any>(response);
|
||||
if (result.status === 'success') {
|
||||
return {
|
||||
@@ -251,7 +295,7 @@ export const apiService = {
|
||||
},
|
||||
|
||||
async saveBackupSettings(settings: BackupSettings): Promise<ApiResponse<null>> {
|
||||
const response = await fetch(`${API_BASE}/api/backup/settings`, {
|
||||
const response = await authFetch(`${API_BASE}/api/backup/settings`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
|
||||
Reference in New Issue
Block a user