ProductionGame/frontend/js/api.js

120 lines
3.6 KiB
JavaScript

// API Communication and State Sync
const API = {
tokenKey: 'el_puro_jwt_token',
userKey: 'el_puro_username',
getToken() {
return localStorage.getItem(this.tokenKey);
},
getUsername() {
return localStorage.getItem(this.userKey);
},
setAuth(token, username) {
localStorage.setItem(this.tokenKey, token);
localStorage.setItem(this.userKey, username);
},
logout() {
localStorage.removeItem(this.tokenKey);
localStorage.removeItem(this.userKey);
},
async register(username, password, websiteHp = '') {
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username,
password,
website_hp: websiteHp
})
});
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Registration failed' }));
throw new Error(err.detail || 'Registration failed');
}
const data = await res.json();
this.setAuth(data.token, data.username);
return data;
},
async login(username, password) {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: 'Invalid credentials' }));
throw new Error(err.detail || 'Login failed');
}
const data = await res.json();
this.setAuth(data.token, data.username);
return data;
},
async loadRemoteSave() {
const token = this.getToken();
if (!token) return null;
try {
const res = await fetch('/api/game/state', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!res.ok) {
if (res.status === 401) this.logout();
return null;
}
const data = await res.json();
return data.has_save ? data.save_data : null;
} catch (e) {
console.warn('Network issue loading cloud save, fallback to local', e);
return null;
}
},
async syncTick(actions = []) {
const token = this.getToken();
if (!token) return null;
try {
const res = await fetch('/api/game/tick', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ actions })
});
if (!res.ok) {
if (res.status === 401) this.logout();
return null;
}
const data = await res.json();
return data.save_data;
} catch (e) {
console.warn('Network tick failed, queuing actions', e);
return null;
}
},
async saveRemote(saveData) {
const token = this.getToken();
if (!token) return;
try {
await fetch('/api/game/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ save_data: saveData })
});
} catch (e) {
console.warn('Failed cloud save sync', e);
}
}
};