- Add OpenTTD server integration (config editor, server card, API) - Add Terraria server integration (config editor, API) - Add legends to all config editors for syntax highlighting - Simplify UserManagement: remove edit/delete buttons, add Discord avatars - Add auto-logout on 401/403 API errors - Update save button styling with visible borders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
313 lines
8.8 KiB
JavaScript
313 lines
8.8 KiB
JavaScript
const API_URL = import.meta.env.VITE_API_URL || '/api'
|
|
|
|
async function fetchAPI(endpoint, options = {}) {
|
|
const response = await fetch(`${API_URL}${endpoint}`, {
|
|
...options,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
},
|
|
})
|
|
|
|
// Auto-logout on auth errors (invalid/expired token)
|
|
if (response.status === 401 || response.status === 403) {
|
|
localStorage.removeItem('gsm_token')
|
|
window.location.href = '/'
|
|
throw new Error('Session expired')
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({ message: 'Request failed' }))
|
|
throw new Error(error.message || `HTTP ${response.status}`)
|
|
}
|
|
|
|
return response.json()
|
|
}
|
|
|
|
// Auth
|
|
export async function login(username, password) {
|
|
return fetchAPI('/auth/login', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password }),
|
|
})
|
|
}
|
|
|
|
export async function getMe(token) {
|
|
return fetchAPI('/auth/me', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function changePassword(currentPassword, newPassword) {
|
|
const token = localStorage.getItem('gsm_token')
|
|
return fetchAPI('/auth/change-password', {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ currentPassword, newPassword }),
|
|
})
|
|
}
|
|
|
|
// Servers
|
|
export async function getServers(token) {
|
|
return fetchAPI('/servers', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function serverAction(token, serverId, action, body = null) {
|
|
return fetchAPI(`/servers/${serverId}/${action}`, {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
...(body && { body: JSON.stringify(body) }),
|
|
})
|
|
}
|
|
|
|
export async function getWhitelist(token, serverId) {
|
|
const headers = token ? { Authorization: `Bearer ${token}` } : {}
|
|
return fetchAPI(`/servers/${serverId}/whitelist`, { headers })
|
|
}
|
|
|
|
export async function sendRcon(token, serverId, command) {
|
|
return fetchAPI(`/servers/${serverId}/rcon`, {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ command }),
|
|
})
|
|
}
|
|
|
|
export async function getServerLogs(token, serverId, lines = 50) {
|
|
return fetchAPI(`/servers/${serverId}/logs?lines=${lines}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
// Metrics
|
|
export async function getMetricsHistory(token, serverId, range = '1h') {
|
|
return fetchAPI(`/servers/${serverId}/metrics/history?range=${range}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
// Users (admin only)
|
|
export async function getUsers(token) {
|
|
return fetchAPI('/auth/users', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function createUser(token, userData) {
|
|
return fetchAPI('/auth/users', {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify(userData),
|
|
})
|
|
}
|
|
|
|
export async function updateUserRole(token, userId, role) {
|
|
return fetchAPI(`/auth/users/${userId}/role`, {
|
|
method: 'PATCH',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ role }),
|
|
})
|
|
}
|
|
|
|
export async function updateUserPassword(token, userId, password) {
|
|
return fetchAPI(`/auth/users/${userId}/password`, {
|
|
method: 'PATCH',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ password }),
|
|
})
|
|
}
|
|
|
|
export async function deleteUser(token, userId) {
|
|
return fetchAPI(`/auth/users/${userId}`, {
|
|
method: 'DELETE',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
// Factorio World Management
|
|
export async function getFactorioSaves(token) {
|
|
return fetchAPI('/servers/factorio/saves', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function getFactorioPresets(token) {
|
|
return fetchAPI('/servers/factorio/presets', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function getFactorioPreset(token, name) {
|
|
return fetchAPI(`/servers/factorio/presets/${name}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function getFactorioTemplates(token) {
|
|
return fetchAPI('/servers/factorio/templates', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function createFactorioTemplate(token, name, settings) {
|
|
return fetchAPI('/servers/factorio/templates', {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ name, settings }),
|
|
})
|
|
}
|
|
|
|
export async function deleteFactorioTemplate(token, id) {
|
|
return fetchAPI(`/servers/factorio/templates/${id}`, {
|
|
method: 'DELETE',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function createFactorioWorld(token, saveName, settings) {
|
|
return fetchAPI('/servers/factorio/create-world', {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ saveName, settings }),
|
|
})
|
|
}
|
|
|
|
export async function deleteFactorioSave(token, saveName) {
|
|
return fetchAPI(`/servers/factorio/saves/${encodeURIComponent(saveName)}`, {
|
|
method: 'DELETE',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function getFactorioCurrentSave(token) {
|
|
return fetchAPI('/servers/factorio/current-save', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function getFactorioWorldSettings(token, saveName) {
|
|
return fetchAPI(`/servers/factorio/saves/${encodeURIComponent(saveName)}/settings`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
// Auto-Shutdown Settings
|
|
export async function getAutoShutdownSettings(token, serverId) {
|
|
return fetchAPI(`/servers/${serverId}/autoshutdown`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function setAutoShutdownSettings(token, serverId, enabled, timeoutMinutes) {
|
|
return fetchAPI(`/servers/${serverId}/autoshutdown`, {
|
|
method: 'PUT',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ enabled, timeoutMinutes }),
|
|
})
|
|
}
|
|
|
|
// Zomboid Config Management
|
|
export async function getZomboidConfigs(token) {
|
|
return fetchAPI('/servers/zomboid/config', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function getZomboidConfig(token, filename) {
|
|
return fetchAPI(`/servers/zomboid/config/${encodeURIComponent(filename)}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function saveZomboidConfig(token, filename, content) {
|
|
return fetchAPI(`/servers/zomboid/config/${encodeURIComponent(filename)}`, {
|
|
method: 'PUT',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ content }),
|
|
})
|
|
}
|
|
|
|
// Palworld Config Management
|
|
export async function getPalworldConfigs(token) {
|
|
return fetchAPI('/servers/palworld/config', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function getPalworldConfig(token, filename) {
|
|
return fetchAPI(`/servers/palworld/config/${encodeURIComponent(filename)}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function savePalworldConfig(token, filename, content) {
|
|
return fetchAPI(`/servers/palworld/config/${encodeURIComponent(filename)}`, {
|
|
method: 'PUT',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ content }),
|
|
})
|
|
}
|
|
|
|
// Terraria Config Management
|
|
export async function getTerrariaConfig(token) {
|
|
return fetchAPI('/servers/terraria/config', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function saveTerrariaConfig(token, content) {
|
|
return fetchAPI('/servers/terraria/config', {
|
|
method: 'PUT',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ content }),
|
|
})
|
|
}
|
|
|
|
// OpenTTD Config Management
|
|
export async function getOpenTTDConfig(token) {
|
|
return fetchAPI('/servers/openttd/config', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function saveOpenTTDConfig(token, content) {
|
|
return fetchAPI('/servers/openttd/config', {
|
|
method: 'PUT',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ content }),
|
|
})
|
|
}
|
|
|
|
// Activity Log
|
|
export async function getActivityLog(token, limit = 100) {
|
|
return fetchAPI(`/servers/activity-log?limit=${limit}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
// Display Settings
|
|
export async function getAllDisplaySettings(token) {
|
|
return fetchAPI('/servers/display-settings', {
|
|
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
})
|
|
}
|
|
|
|
export async function getDisplaySettings(token, serverId) {
|
|
return fetchAPI(`/servers/${serverId}/display-settings`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
}
|
|
|
|
export async function saveDisplaySettings(token, serverId, address, hint) {
|
|
return fetchAPI(`/servers/${serverId}/display-settings`, {
|
|
method: 'PUT',
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ address, hint }),
|
|
})
|
|
}
|
|
|
|
// Alias for backwards compatibility
|
|
export const setDisplaySettings = saveDisplaySettings
|