Add files via upload
This commit is contained in:
commit
675493bafc
28 changed files with 2176 additions and 0 deletions
125
services/api.ts
Normal file
125
services/api.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import * as SecureStore from 'expo-secure-store';
|
||||
|
||||
const BASE = 'https://music.nodeshift.space';
|
||||
const CLOUD = 'https://cloud.nodeshift.space';
|
||||
const CLIENT_ID = 'nsc_A5zi4cZ103aYJGcNTG1bERXV1YCAQW_U';
|
||||
const REDIRECT = 'burmalda://auth/callback';
|
||||
|
||||
// ── Token storage ──────────────────────────────────────────────────────────────
|
||||
|
||||
export async function saveTokens(access: string, refresh: string) {
|
||||
await SecureStore.setItemAsync('access_token', access);
|
||||
await SecureStore.setItemAsync('refresh_token', refresh);
|
||||
}
|
||||
|
||||
export async function getAccessToken(): Promise<string | null> {
|
||||
return SecureStore.getItemAsync('access_token');
|
||||
}
|
||||
|
||||
export async function clearTokens() {
|
||||
await SecureStore.deleteItemAsync('access_token');
|
||||
await SecureStore.deleteItemAsync('refresh_token');
|
||||
}
|
||||
|
||||
// ── Auth fetch helper ──────────────────────────────────────────────────────────
|
||||
|
||||
async function apiFetch(path: string, opts: RequestInit = {}): Promise<Response> {
|
||||
const token = await getAccessToken();
|
||||
const headers: HeadersInit = {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...(opts.headers as Record<string, string> || {}),
|
||||
};
|
||||
return fetch(`${BASE}${path}`, { ...opts, headers });
|
||||
}
|
||||
|
||||
// ── OAuth ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
export function buildOAuthUrl(state: string): string {
|
||||
const p = new URLSearchParams({
|
||||
client_id: CLIENT_ID,
|
||||
redirect_uri: REDIRECT,
|
||||
state,
|
||||
scope: 'profile email',
|
||||
});
|
||||
return `${CLOUD}/api/cloud/oauth/authorize?${p}`;
|
||||
}
|
||||
|
||||
export async function exchangeCode(code: string): Promise<{ access_token: string; refresh_token: string; user: User }> {
|
||||
const res = await fetch(`${BASE}/api/auth/cloud-login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ code, redirect_uri: REDIRECT }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
throw new Error(err.detail || 'Auth failed');
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
// ── Types ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface Track {
|
||||
id: number;
|
||||
title: string;
|
||||
artist: string;
|
||||
duration: number;
|
||||
cover_url: string | null;
|
||||
stream_url: string;
|
||||
album?: string;
|
||||
plays?: number;
|
||||
liked?: boolean;
|
||||
}
|
||||
|
||||
export interface Album {
|
||||
id: number;
|
||||
title: string;
|
||||
artist: string;
|
||||
cover_url: string | null;
|
||||
track_count: number;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
email: string;
|
||||
username: string | null;
|
||||
avatar_url: string | null;
|
||||
plan: string;
|
||||
}
|
||||
|
||||
// ── API calls ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function getTracks(): Promise<Track[]> {
|
||||
const r = await apiFetch('/api/music/tracks');
|
||||
if (!r.ok) return [];
|
||||
return r.json();
|
||||
}
|
||||
|
||||
export async function getAlbums(): Promise<Album[]> {
|
||||
const r = await apiFetch('/api/music/albums');
|
||||
if (!r.ok) return [];
|
||||
return r.json();
|
||||
}
|
||||
|
||||
export async function getMe(): Promise<User | null> {
|
||||
const r = await apiFetch('/api/music/profile/me');
|
||||
if (!r.ok) return null;
|
||||
return r.json();
|
||||
}
|
||||
|
||||
export async function searchTracks(q: string): Promise<Track[]> {
|
||||
const r = await apiFetch(`/api/music/tracks?q=${encodeURIComponent(q)}`);
|
||||
if (!r.ok) return [];
|
||||
return r.json();
|
||||
}
|
||||
|
||||
export function streamUrl(filename: string): string {
|
||||
return `${BASE}/api/music/stream/${filename}`;
|
||||
}
|
||||
|
||||
export function coverUrl(filename: string | null): string | null {
|
||||
if (!filename) return null;
|
||||
if (filename.startsWith('http')) return filename;
|
||||
return `${BASE}/api/music/cover/${filename}`;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue