mirror of
https://github.com/revanced/revanced-website.git
synced 2025-05-02 15:44:25 +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
|
node_modules
|
||||||
|
openapi.json
|
||||||
|
|
||||||
# Output
|
# Output
|
||||||
.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