chore: scaffold Rantii project with README, icons, CSS, and JS directory structure
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* @fileoverview DevRant API Client for Rantii
|
||||
* @author retoor <retoor@molodetz.nl>
|
||||
* @description HTTP client for communicating with the DevRant API
|
||||
* @keywords api, client, devrant, http, fetch
|
||||
*/
|
||||
|
||||
const API_BASE_URL = window.location.hostname === 'localhost' ? '/api/' : 'https://dr.molodetz.nl/api/';
|
||||
const APP_ID = 3;
|
||||
|
||||
class ApiClient {
|
||||
constructor() {
|
||||
this.tokenId = null;
|
||||
this.tokenKey = null;
|
||||
this.userId = null;
|
||||
}
|
||||
|
||||
setAuth(tokenId, tokenKey, userId) {
|
||||
this.tokenId = tokenId;
|
||||
this.tokenKey = tokenKey;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
clearAuth() {
|
||||
this.tokenId = null;
|
||||
this.tokenKey = null;
|
||||
this.userId = null;
|
||||
}
|
||||
|
||||
isAuthenticated() {
|
||||
return this.tokenId !== null && this.tokenKey !== null;
|
||||
}
|
||||
|
||||
buildParams(params = {}) {
|
||||
const result = { app: APP_ID, ...params };
|
||||
if (this.isAuthenticated()) {
|
||||
result.token_id = this.tokenId;
|
||||
result.token_key = this.tokenKey;
|
||||
result.user_id = this.userId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
buildUrl(endpoint) {
|
||||
const path = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint;
|
||||
return API_BASE_URL + path;
|
||||
}
|
||||
|
||||
async request(method, endpoint, params = {}, timeout = 30000) {
|
||||
const url = this.buildUrl(endpoint);
|
||||
const allParams = this.buildParams(params);
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||
|
||||
const config = {
|
||||
method,
|
||||
headers: {},
|
||||
signal: controller.signal
|
||||
};
|
||||
|
||||
try {
|
||||
let fullUrl = url;
|
||||
if (method === 'GET' || method === 'DELETE') {
|
||||
const queryString = new URLSearchParams(allParams).toString();
|
||||
fullUrl = queryString ? `${url}?${queryString}` : url;
|
||||
} else {
|
||||
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
config.body = new URLSearchParams(allParams).toString();
|
||||
}
|
||||
|
||||
const response = await fetch(fullUrl, config);
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok && response.status >= 500) {
|
||||
return { success: false, error: `Server error: ${response.status}` };
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
if (error.name === 'AbortError') {
|
||||
return { success: false, error: 'Request timed out' };
|
||||
}
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async get(endpoint, params = {}) {
|
||||
return this.request('GET', endpoint, params);
|
||||
}
|
||||
|
||||
async post(endpoint, params = {}) {
|
||||
return this.request('POST', endpoint, params);
|
||||
}
|
||||
|
||||
async delete(endpoint, params = {}) {
|
||||
return this.request('DELETE', endpoint, params);
|
||||
}
|
||||
|
||||
async login(username, password) {
|
||||
const response = await this.post('/users/auth-token', {
|
||||
username,
|
||||
password
|
||||
});
|
||||
if (response.success && response.auth_token) {
|
||||
this.setAuth(
|
||||
response.auth_token.id,
|
||||
response.auth_token.key,
|
||||
response.auth_token.user_id
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
authToken: response.auth_token
|
||||
};
|
||||
}
|
||||
return { success: false, error: response.error || 'Login failed' };
|
||||
}
|
||||
|
||||
async getRants(sort = 'recent', limit = 20, skip = 0) {
|
||||
const response = await this.get('/devrant/rants', { sort, limit, skip });
|
||||
if (response.success) {
|
||||
return { success: true, rants: response.rants || [] };
|
||||
}
|
||||
return { success: false, rants: [], error: response.error };
|
||||
}
|
||||
|
||||
async getRant(rantId) {
|
||||
const response = await this.get(`/devrant/rants/${rantId}`);
|
||||
if (response.success) {
|
||||
return {
|
||||
success: true,
|
||||
rant: response.rant,
|
||||
comments: response.comments || []
|
||||
};
|
||||
}
|
||||
return { success: false, error: response.error };
|
||||
}
|
||||
|
||||
async voteRant(rantId, vote, reason = null) {
|
||||
const params = { vote };
|
||||
if (reason !== null) {
|
||||
params.reason = reason;
|
||||
}
|
||||
const response = await this.post(`/devrant/rants/${rantId}/vote`, params);
|
||||
return { success: response.success, rant: response.rant };
|
||||
}
|
||||
|
||||
async postComment(rantId, comment) {
|
||||
const response = await this.post(`/devrant/rants/${rantId}/comments`, {
|
||||
comment,
|
||||
plat: 2
|
||||
});
|
||||
return { success: response.success, comment: response.comment };
|
||||
}
|
||||
|
||||
async updateComment(commentId, comment) {
|
||||
const response = await this.post(`/comments/${commentId}`, { comment });
|
||||
return { success: response.success };
|
||||
}
|
||||
|
||||
async deleteComment(commentId) {
|
||||
const response = await this.delete(`/comments/${commentId}`);
|
||||
return { success: response.success };
|
||||
}
|
||||
|
||||
async voteComment(commentId, vote, reason = null) {
|
||||
const params = { vote };
|
||||
if (reason !== null) {
|
||||
params.reason = reason;
|
||||
}
|
||||
const response = await this.post(`/comments/${commentId}/vote`, params);
|
||||
return { success: response.success, comment: response.comment };
|
||||
}
|
||||
|
||||
async getComment(commentId) {
|
||||
const response = await this.get(`/comments/${commentId}`);
|
||||
if (response.success) {
|
||||
return { success: true, comment: response.comment };
|
||||
}
|
||||
return { success: false, error: response.error };
|
||||
}
|
||||
|
||||
async getUserId(username) {
|
||||
const response = await this.get('/get-user-id', { username });
|
||||
if (response.success) {
|
||||
return { success: true, userId: response.user_id };
|
||||
}
|
||||
return { success: false, error: response.error };
|
||||
}
|
||||
|
||||
async getProfile(userId) {
|
||||
const response = await this.get(`/users/${userId}`);
|
||||
if (response.success) {
|
||||
return { success: true, profile: response.profile };
|
||||
}
|
||||
return { success: false, error: response.error };
|
||||
}
|
||||
|
||||
async getProfileByUsername(username) {
|
||||
const userIdResponse = await this.getUserId(username);
|
||||
if (!userIdResponse.success) {
|
||||
return { success: false, error: userIdResponse.error };
|
||||
}
|
||||
return this.getProfile(userIdResponse.userId);
|
||||
}
|
||||
|
||||
async search(term) {
|
||||
const response = await this.get('/devrant/search', { term });
|
||||
if (response.success) {
|
||||
return { success: true, rants: response.results || [] };
|
||||
}
|
||||
return { success: false, rants: [], error: response.error };
|
||||
}
|
||||
|
||||
async getNotifications() {
|
||||
const response = await this.get('/users/me/notif-feed', { ext_prof: 1 });
|
||||
if (response.success) {
|
||||
const items = response.data?.items || [];
|
||||
const users = response.data?.username_map || {};
|
||||
|
||||
const notifications = items.map(item => ({
|
||||
...item,
|
||||
username: users[item.uid] || item.username || 'Someone'
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
notifications,
|
||||
unread: response.data?.unread || { total: 0 }
|
||||
};
|
||||
}
|
||||
return { success: false, notifications: [], error: response.error };
|
||||
}
|
||||
|
||||
async clearNotifications() {
|
||||
const response = await this.delete('/users/me/notif-feed');
|
||||
return { success: response.success };
|
||||
}
|
||||
|
||||
async postRant(text, tags = '', type = 1) {
|
||||
const response = await this.post('/devrant/rants', {
|
||||
rant: text,
|
||||
tags,
|
||||
type,
|
||||
plat: 2
|
||||
});
|
||||
return { success: response.success, rantId: response.rant_id };
|
||||
}
|
||||
|
||||
async deleteRant(rantId) {
|
||||
const response = await this.delete(`/devrant/rants/${rantId}`);
|
||||
return { success: response.success };
|
||||
}
|
||||
|
||||
async getWeeklyRants(sort = 'recent', limit = 20, skip = 0) {
|
||||
const response = await this.get('/devrant/weekly-rants', { sort, limit, skip });
|
||||
if (response.success) {
|
||||
return { success: true, rants: response.rants || [] };
|
||||
}
|
||||
return { success: false, rants: [], error: response.error };
|
||||
}
|
||||
|
||||
async getCollabs(sort = 'recent', limit = 20, skip = 0) {
|
||||
const response = await this.get('/devrant/collabs', { sort, limit, skip });
|
||||
if (response.success) {
|
||||
return { success: true, rants: response.rants || [] };
|
||||
}
|
||||
return { success: false, rants: [], error: response.error };
|
||||
}
|
||||
|
||||
async getStories(sort = 'recent', limit = 20, skip = 0) {
|
||||
const response = await this.get('/devrant/story-rants', { sort, limit, skip });
|
||||
if (response.success) {
|
||||
return { success: true, rants: response.rants || [] };
|
||||
}
|
||||
return { success: false, rants: [], error: response.error };
|
||||
}
|
||||
}
|
||||
|
||||
export { ApiClient, API_BASE_URL, APP_ID };
|
||||
Reference in New Issue
Block a user