mirror of
https://github.com/revanced/revanced-website.git
synced 2025-04-29 22:24:31 +02:00
feat: begin to add API interfaces
This commit is contained in:
parent
f0c2ff64ed
commit
0c25b35e25
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
node_modules
|
||||
openapi.json
|
||||
|
||||
# Output
|
||||
.output
|
||||
|
45
src/lib/api/interfaces/index.ts
Normal file
45
src/lib/api/interfaces/index.ts
Normal file
@ -0,0 +1,45 @@
|
||||
// API service interfaces
|
||||
|
||||
export interface AnnouncementsApi {
|
||||
getAnnouncements(
|
||||
cursor?: number,
|
||||
count?: number,
|
||||
tag?: string
|
||||
): Promise<ApiResponseAnnouncement[]>;
|
||||
getLatestAnnouncement(tag?: string): Promise<ApiResponseAnnouncement[]>;
|
||||
getLatestAnnouncementIds(tag?: string): Promise<ApiResponseAnnouncement[]>;
|
||||
getAnnouncement(id: number): Promise<ApiResponseAnnouncement>;
|
||||
createAnnouncement(announcement: ApiAnnouncement, token: string): Promise<void>;
|
||||
updateAnnouncement(id: number, announcement: ApiAnnouncement, token: string): Promise<void>;
|
||||
deleteAnnouncement(id: number, token: string): Promise<void>;
|
||||
getAnnouncementTags(): Promise<string[]>;
|
||||
}
|
||||
|
||||
export interface PatchesApi {
|
||||
getCurrentRelease(prerelease?: boolean): Promise<ApiRelease>;
|
||||
getCurrentReleaseVersion(prerelease?: boolean): Promise<ApiReleaseVersion>;
|
||||
getPatchesList(prerelease?: boolean): Promise<string>;
|
||||
getPublicKeys(): Promise<ApiAssetPublicKey>;
|
||||
}
|
||||
|
||||
export interface ManagerApi {
|
||||
getCurrentRelease(prerelease?: boolean): Promise<ApiRelease>;
|
||||
getCurrentReleaseVersion(prerelease?: boolean): Promise<ApiReleaseVersion>;
|
||||
}
|
||||
|
||||
export interface GeneralApi {
|
||||
getToken(authDigest: string): Promise<ApiToken>;
|
||||
getContributors(): Promise<APIContributable[]>;
|
||||
getTeamMembers(): Promise<ApiMember[]>;
|
||||
getAbout(): Promise<APIAbout>;
|
||||
ping(): Promise<boolean>;
|
||||
getRateLimit(): Promise<ApiRateLimit>;
|
||||
}
|
||||
|
||||
// unified API interface
|
||||
export interface RevancedApi {
|
||||
announcements: AnnouncementsApi;
|
||||
patches: PatchesApi;
|
||||
manager: ManagerApi;
|
||||
general: GeneralApi;
|
||||
}
|
133
src/lib/api/models.ts
Normal file
133
src/lib/api/models.ts
Normal file
@ -0,0 +1,133 @@
|
||||
export type BackendResponseAnnouncement = {
|
||||
archived_at?: Date;
|
||||
attachments?: string[];
|
||||
author?: string;
|
||||
content?: string;
|
||||
created_at: Date;
|
||||
id: number;
|
||||
tags?: string[];
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type BackendAnnouncement = {
|
||||
archived_at?: Date;
|
||||
attachments?: string[];
|
||||
author?: string;
|
||||
content?: string;
|
||||
created_at?: Date;
|
||||
level?: number;
|
||||
tags?: string[];
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type BackendLatestPatchesRelease = {
|
||||
created_at: Date;
|
||||
description: string;
|
||||
download_url: string;
|
||||
signature_download_url?: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
export type BackendLatestPatchesVersion = {
|
||||
version: string;
|
||||
};
|
||||
|
||||
export type BackendCompatiblePackage = Record<string, string[]>;
|
||||
export type BackendPatchOptionValue = Record<string, string | number>;
|
||||
export type BackendPatchOption = {
|
||||
title: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
values?: BackendPatchOptionValue[];
|
||||
};
|
||||
export type BackendPatch = {
|
||||
name: string;
|
||||
description: string;
|
||||
compatiblePackages: BackendCompatiblePackage;
|
||||
options: BackendPatchOption[];
|
||||
};
|
||||
|
||||
// TODO
|
||||
|
||||
export type BackendAssetPublicKey = {
|
||||
patches_public_key: string;
|
||||
};
|
||||
|
||||
export type BackendToken = {
|
||||
token: string;
|
||||
};
|
||||
|
||||
export type BackendContributor = {
|
||||
avatar_url: string;
|
||||
contributions: number;
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type BackendContributable = {
|
||||
contributors: BackendContributor[];
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type BackendGpgKey = {
|
||||
id: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type BackendMember = {
|
||||
avatar_url: string;
|
||||
bio?: string;
|
||||
gpg_key?: BackendGpgKey;
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type BackendAboutBranding = {
|
||||
logo: string;
|
||||
};
|
||||
|
||||
export type BackendAboutContact = {
|
||||
email: string;
|
||||
};
|
||||
|
||||
export type BackendAboutLink = {
|
||||
name: string;
|
||||
preferred?: boolean;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type BackendAboutWallet = {
|
||||
address: string;
|
||||
currency_code: string;
|
||||
network: string;
|
||||
preferred?: boolean;
|
||||
};
|
||||
|
||||
export type BackendAboutDonations = {
|
||||
links?: BackendAboutLink[];
|
||||
wallets?: BackendAboutWallet[];
|
||||
};
|
||||
|
||||
export type BackendAboutSocial = {
|
||||
name: string;
|
||||
preferred?: boolean;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type BackendAbout = {
|
||||
about: string;
|
||||
branding?: BackendAboutBranding;
|
||||
contact?: BackendAboutContact;
|
||||
donations?: BackendAboutDonations;
|
||||
keys: string;
|
||||
name: string;
|
||||
socials?: BackendAboutSocial[];
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type BackendRateLimit = {
|
||||
limit: number;
|
||||
remaining: number;
|
||||
reset: Date;
|
||||
};
|
320
src/lib/api/services/index.ts
Normal file
320
src/lib/api/services/index.ts
Normal file
@ -0,0 +1,320 @@
|
||||
import type {
|
||||
AnnouncementsApi,
|
||||
PatchesApi,
|
||||
ManagerApi,
|
||||
GeneralApi,
|
||||
RevancedApi,
|
||||
ApiResponseAnnouncement,
|
||||
ApiAnnouncement,
|
||||
ApiRelease,
|
||||
ApiReleaseVersion,
|
||||
ApiAssetPublicKey,
|
||||
ApiToken,
|
||||
APIContributable,
|
||||
ApiMember,
|
||||
APIAbout,
|
||||
ApiRateLimit
|
||||
} from '../interfaces';
|
||||
|
||||
// Base URL for the API
|
||||
const API_BASE_URL = 'https://api.revanced.app';
|
||||
|
||||
// Helper function to build URLs with query parameters
|
||||
function buildUrl(
|
||||
path: string,
|
||||
params?: Record<string, string | number | boolean | undefined>
|
||||
): string {
|
||||
const url = new URL(`${API_BASE_URL}${path}`);
|
||||
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
// Implementation of AnnouncementsApi
|
||||
class RevancedAnnouncementsApi implements AnnouncementsApi {
|
||||
async getAnnouncements(
|
||||
cursor?: number,
|
||||
count?: number,
|
||||
tag?: string
|
||||
): Promise<ApiResponseAnnouncement[]> {
|
||||
const url = buildUrl('/v4/announcements', { cursor, count, tag });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch announcements: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getLatestAnnouncement(tag?: string): Promise<ApiResponseAnnouncement[]> {
|
||||
const url = buildUrl('/v4/announcements/latest', { tag });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch latest announcement: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getLatestAnnouncementIds(tag?: string): Promise<ApiResponseAnnouncement[]> {
|
||||
const url = buildUrl('/v4/announcements/latest/id', { tag });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch latest announcement ids: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getAnnouncement(id: number): Promise<ApiResponseAnnouncement> {
|
||||
const url = buildUrl(`/v4/announcements/${id}`);
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch announcement: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async createAnnouncement(announcement: ApiAnnouncement, token: string): Promise<void> {
|
||||
const url = buildUrl('/v4/announcements');
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(announcement)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to create announcement: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
async updateAnnouncement(
|
||||
id: number,
|
||||
announcement: ApiAnnouncement,
|
||||
token: string
|
||||
): Promise<void> {
|
||||
const url = buildUrl(`/v4/announcements/${id}`);
|
||||
const response = await fetch(url, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(announcement)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to update announcement: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAnnouncement(id: number, token: string): Promise<void> {
|
||||
const url = buildUrl(`/v4/announcements/${id}`);
|
||||
const response = await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to delete announcement: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
async getAnnouncementTags(): Promise<string[]> {
|
||||
const url = buildUrl('/v4/announcements/tags');
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch announcement tags: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of PatchesApi
|
||||
class RevancedPatchesApi implements PatchesApi {
|
||||
async getCurrentRelease(prerelease?: boolean): Promise<ApiRelease> {
|
||||
const url = buildUrl('/v4/patches', { prerelease });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch current patches release: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getCurrentReleaseVersion(prerelease?: boolean): Promise<ApiReleaseVersion> {
|
||||
const url = buildUrl('/v4/patches/version', { prerelease });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch current patches version: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getPatchesList(prerelease?: boolean): Promise<string> {
|
||||
const url = buildUrl('/v4/patches/list', { prerelease });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch patches list: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getPublicKeys(): Promise<ApiAssetPublicKey> {
|
||||
const url = buildUrl('/v4/patches/keys');
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch patches public keys: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of ManagerApi
|
||||
class RevancedManagerApi implements ManagerApi {
|
||||
async getCurrentRelease(prerelease?: boolean): Promise<ApiRelease> {
|
||||
const url = buildUrl('/v4/manager', { prerelease });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch current manager release: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getCurrentReleaseVersion(prerelease?: boolean): Promise<ApiReleaseVersion> {
|
||||
const url = buildUrl('/v4/manager/version', { prerelease });
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch current manager version: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of GeneralApi
|
||||
class RevancedGeneralApi implements GeneralApi {
|
||||
async getToken(authDigest: string): Promise<ApiToken> {
|
||||
const url = buildUrl('/v4/token');
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: authDigest
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to get token: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getContributors(): Promise<APIContributable[]> {
|
||||
const url = buildUrl('/v4/contributors');
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch contributors: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getTeamMembers(): Promise<ApiMember[]> {
|
||||
const url = buildUrl('/v4/team');
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch team members: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async getAbout(): Promise<APIAbout> {
|
||||
const url = buildUrl('/v4/about');
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch about: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async ping(): Promise<boolean> {
|
||||
const url = buildUrl('/v4/ping');
|
||||
const response = await fetch(url, { method: 'HEAD' });
|
||||
return response.ok;
|
||||
}
|
||||
|
||||
async getRateLimit(): Promise<ApiRateLimit> {
|
||||
const url = buildUrl('/v4/backend/rate_limit');
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch rate limit: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// Main implementation of the RevancedApi interface
|
||||
export class RevancedApiClient implements RevancedApi {
|
||||
public announcements: AnnouncementsApi;
|
||||
public patches: PatchesApi;
|
||||
public manager: ManagerApi;
|
||||
public general: GeneralApi;
|
||||
|
||||
constructor() {
|
||||
this.announcements = new RevancedAnnouncementsApi();
|
||||
this.patches = new RevancedPatchesApi();
|
||||
this.manager = new RevancedManagerApi();
|
||||
this.general = new RevancedGeneralApi();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a singleton instance for easy importing
|
||||
export const revancedApi = new RevancedApiClient();
|
||||
|
||||
// Example of how to use the API with dependency injection
|
||||
export async function fetchTeamMembers(api: RevancedApi): Promise<ApiMember[]> {
|
||||
return await api.general.getTeamMembers();
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
// const teamMembers = await fetchTeamMembers(revancedApi);
|
||||
// or for testing:
|
||||
// const mockApi = new MockRevancedApi();
|
||||
// const teamMembers = await fetchTeamMembers(mockApi);
|
@ -1 +0,0 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
Loading…
x
Reference in New Issue
Block a user