erlc-api 3.3.2-a → 3.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -24
- package/README_ES.md +43 -23
- package/package.json +1 -1
- package/src/classes/client.js +108 -6
- package/src/functions/server/getServer.js +5 -0
- package/src/functions/server/requestServer.js +17 -11
- package/src/functions/server/runCommand.js +5 -2
- package/src/types/index.d.ts +32 -11
- package/tests/cache.test.js +55 -0
package/README.md
CHANGED
|
@@ -30,53 +30,60 @@ bun add erlc-api
|
|
|
30
30
|
|
|
31
31
|
### Initialization
|
|
32
32
|
|
|
33
|
-
You can
|
|
33
|
+
You can initialize the client with your `Server Key` so every server request can reuse it automatically. A `Global Token` is optional and only required for large-scale applications.
|
|
34
34
|
|
|
35
35
|
**JavaScript**
|
|
36
36
|
```javascript
|
|
37
37
|
const erlc = require("erlc-api");
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const client = new erlc.Client({
|
|
40
|
+
serverToken: "your-server-key-here",
|
|
41
|
+
// globalToken: "your-global-token-here", // Optional, for large apps
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await client.ready;
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
if (!client.connected) {
|
|
47
|
+
console.error("Could not connect:", client.connectionError.message);
|
|
48
|
+
}
|
|
44
49
|
```
|
|
45
50
|
|
|
46
51
|
**TypeScript**
|
|
47
52
|
```typescript
|
|
48
|
-
import { Client
|
|
53
|
+
import { Client } from "erlc-api";
|
|
49
54
|
|
|
50
|
-
const client = new Client(
|
|
55
|
+
const client = new Client({
|
|
56
|
+
serverToken: "your-server-key-here",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
await client.ready;
|
|
51
60
|
```
|
|
52
61
|
|
|
53
62
|
---
|
|
54
63
|
|
|
55
64
|
## 📖 Usage Examples
|
|
56
65
|
|
|
57
|
-
Make sure to have your `Server Key` ready (get it from your private server settings in ER:LC).
|
|
66
|
+
Make sure to have your `Server Key` ready (get it from your private server settings in ER:LC). When the key is configured in `Client`, you do not need to pass it again on every request.
|
|
58
67
|
|
|
59
68
|
### 🖥️ Server Information
|
|
60
69
|
|
|
61
70
|
```javascript
|
|
62
|
-
const serverToken = "your-server-key-here";
|
|
63
|
-
|
|
64
71
|
// Get server status
|
|
65
|
-
const server = await
|
|
72
|
+
const server = await client.getServer();
|
|
66
73
|
console.log(`Server: ${server.Name} | Players: ${server.CurrentPlayers}/${server.MaxPlayers}`);
|
|
67
74
|
|
|
68
75
|
// Get connected players
|
|
69
|
-
const players = await
|
|
76
|
+
const players = await client.getPlayers();
|
|
70
77
|
console.table(players); // Shows name, ID, permission, and team
|
|
71
78
|
|
|
72
79
|
// Get vehicles on the map
|
|
73
|
-
const vehicles = await
|
|
80
|
+
const vehicles = await client.getVehicles();
|
|
74
81
|
|
|
75
82
|
// Get emergency calls
|
|
76
|
-
const emergencyCalls = await
|
|
83
|
+
const emergencyCalls = await client.getEmergencyCalls();
|
|
77
84
|
|
|
78
85
|
// Fetch server info with v2 include flags in one request
|
|
79
|
-
const fullServer = await
|
|
86
|
+
const fullServer = await client.getServer({
|
|
80
87
|
players: true,
|
|
81
88
|
staff: true,
|
|
82
89
|
emergencyCalls: true,
|
|
@@ -89,29 +96,29 @@ Access your server's activity history:
|
|
|
89
96
|
|
|
90
97
|
```javascript
|
|
91
98
|
// Join/Leave Logs
|
|
92
|
-
const joinLogs = await
|
|
99
|
+
const joinLogs = await client.getJoinLogs();
|
|
93
100
|
|
|
94
101
|
// Kill Logs (Killfeed)
|
|
95
|
-
const killLogs = await
|
|
102
|
+
const killLogs = await client.getKillLogs();
|
|
96
103
|
|
|
97
104
|
// Command Logs
|
|
98
|
-
const commandLogs = await
|
|
105
|
+
const commandLogs = await client.getCommandLogs();
|
|
99
106
|
|
|
100
107
|
// Mod Call Logs
|
|
101
|
-
const modCalls = await
|
|
108
|
+
const modCalls = await client.getModcallLogs();
|
|
102
109
|
```
|
|
103
110
|
|
|
104
111
|
### 🛠️ Management & Administration
|
|
105
112
|
|
|
106
113
|
```javascript
|
|
107
114
|
// Get Ban List
|
|
108
|
-
const bans = await
|
|
115
|
+
const bans = await client.getBans();
|
|
109
116
|
|
|
110
117
|
// Get Server Staff
|
|
111
|
-
const staff = await
|
|
118
|
+
const staff = await client.getStaff();
|
|
112
119
|
|
|
113
120
|
// Get Queue
|
|
114
|
-
const queue = await
|
|
121
|
+
const queue = await client.getQueue();
|
|
115
122
|
```
|
|
116
123
|
|
|
117
124
|
### ⚡ Run Command
|
|
@@ -119,10 +126,19 @@ const queue = await erlc.getQueue(serverToken);
|
|
|
119
126
|
Execute commands directly from your code:
|
|
120
127
|
|
|
121
128
|
```javascript
|
|
122
|
-
const command = await
|
|
129
|
+
const command = await client.runCommand(":announce This is an API test!");
|
|
123
130
|
console.log(command); // Returns true if successful
|
|
124
131
|
```
|
|
125
132
|
|
|
133
|
+
### Manual Token Usage
|
|
134
|
+
|
|
135
|
+
You can still pass the server token directly if you prefer the older style:
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
const server = await erlc.getServer("your-server-key-here");
|
|
139
|
+
const players = await erlc.getPlayers("your-server-key-here");
|
|
140
|
+
```
|
|
141
|
+
|
|
126
142
|
---
|
|
127
143
|
|
|
128
144
|
## ⚠️ Error Handling
|
|
@@ -131,7 +147,8 @@ The library throws descriptive errors. You should wrap your calls in `try/catch`
|
|
|
131
147
|
|
|
132
148
|
```javascript
|
|
133
149
|
try {
|
|
134
|
-
const
|
|
150
|
+
const client = new erlc.Client({ serverToken: "invalid-token" });
|
|
151
|
+
await client.connect();
|
|
135
152
|
} catch (error) {
|
|
136
153
|
console.error(error.message); // e.g., "Forbidden: Access denied..."
|
|
137
154
|
}
|
package/README_ES.md
CHANGED
|
@@ -30,57 +30,64 @@ bun add erlc-api
|
|
|
30
30
|
|
|
31
31
|
### Inicialización
|
|
32
32
|
|
|
33
|
-
Puedes
|
|
33
|
+
Puedes iniciar el cliente con tu `Server Key` para reutilizarla automáticamente en todas las peticiones del servidor. El `Global Token` es opcional y solo se requiere para aplicaciones a gran escala.
|
|
34
34
|
|
|
35
35
|
**JavaScript**
|
|
36
36
|
|
|
37
37
|
```javascript
|
|
38
38
|
const erlc = require("erlc-api");
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const client = new erlc.Client({
|
|
41
|
+
serverToken: "tu-server-key-aqui",
|
|
42
|
+
// globalToken: "tu-global-token-aqui", // Opcional, para apps grandes
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await client.ready;
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
if (!client.connected) {
|
|
48
|
+
console.error("No se pudo conectar:", client.connectionError.message);
|
|
49
|
+
}
|
|
45
50
|
```
|
|
46
51
|
|
|
47
52
|
**TypeScript**
|
|
48
53
|
|
|
49
54
|
```typescript
|
|
50
|
-
import { Client
|
|
55
|
+
import { Client } from "erlc-api";
|
|
51
56
|
|
|
52
|
-
const client = new Client(
|
|
57
|
+
const client = new Client({
|
|
58
|
+
serverToken: "tu-server-key-aqui",
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await client.ready;
|
|
53
62
|
```
|
|
54
63
|
|
|
55
64
|
---
|
|
56
65
|
|
|
57
66
|
## 📖 Ejemplos de Uso
|
|
58
67
|
|
|
59
|
-
Asegúrate de tener tu `Server Key` a mano (obtenla en los ajustes de tu servidor privado en ER:LC).
|
|
68
|
+
Asegúrate de tener tu `Server Key` a mano (obtenla en los ajustes de tu servidor privado en ER:LC). Cuando la key está configurada en `Client`, no necesitas volver a pasarla en cada petición.
|
|
60
69
|
|
|
61
70
|
### 🖥️ Información del Servidor
|
|
62
71
|
|
|
63
72
|
```javascript
|
|
64
|
-
const serverToken = "tu-server-key-aqui";
|
|
65
|
-
|
|
66
73
|
// Obtener estado del servidor
|
|
67
|
-
const server = await
|
|
74
|
+
const server = await client.getServer();
|
|
68
75
|
console.log(
|
|
69
76
|
`Servidor: ${server.Name} | Jugadores: ${server.CurrentPlayers}/${server.MaxPlayers}`,
|
|
70
77
|
);
|
|
71
78
|
|
|
72
79
|
// Obtener jugadores conectados
|
|
73
|
-
const players = await
|
|
80
|
+
const players = await client.getPlayers();
|
|
74
81
|
console.table(players); // Muestra nombre, ID, permisos y equipo
|
|
75
82
|
|
|
76
83
|
// Obtener vehículos en el mapa
|
|
77
|
-
const vehicles = await
|
|
84
|
+
const vehicles = await client.getVehicles();
|
|
78
85
|
|
|
79
86
|
// Obtener llamadas de emergencia
|
|
80
|
-
const emergencyCalls = await
|
|
87
|
+
const emergencyCalls = await client.getEmergencyCalls();
|
|
81
88
|
|
|
82
89
|
// Obtener información del servidor con includes de v2 en una sola petición
|
|
83
|
-
const fullServer = await
|
|
90
|
+
const fullServer = await client.getServer({
|
|
84
91
|
players: true,
|
|
85
92
|
staff: true,
|
|
86
93
|
emergencyCalls: true,
|
|
@@ -93,38 +100,50 @@ Accede a los historiales de actividad de tu servidor:
|
|
|
93
100
|
|
|
94
101
|
```javascript
|
|
95
102
|
// Logs de Entradas/Salidas
|
|
96
|
-
const joinLogs = await
|
|
103
|
+
const joinLogs = await client.getJoinLogs();
|
|
97
104
|
|
|
98
105
|
// Logs de Muertes (Killfeed)
|
|
99
|
-
const killLogs = await
|
|
106
|
+
const killLogs = await client.getKillLogs();
|
|
100
107
|
|
|
101
108
|
// Logs de Comandos ejecutados
|
|
102
|
-
const commandLogs = await
|
|
109
|
+
const commandLogs = await client.getCommandLogs();
|
|
103
110
|
|
|
104
111
|
// Logs de Llamadas a Moderadores
|
|
105
|
-
const modCalls = await
|
|
112
|
+
const modCalls = await client.getModcallLogs();
|
|
106
113
|
```
|
|
107
114
|
|
|
108
115
|
### 🛠️ Gestión y Administración
|
|
109
116
|
|
|
110
117
|
```javascript
|
|
111
118
|
// Ver lista de Baneos
|
|
112
|
-
const bans = await
|
|
119
|
+
const bans = await client.getBans();
|
|
113
120
|
|
|
114
121
|
// Obtener Staff del servidor
|
|
115
|
-
const staff = await
|
|
122
|
+
const staff = await client.getStaff();
|
|
123
|
+
|
|
124
|
+
// Obtener cola del servidor
|
|
125
|
+
const queue = await client.getQueue();
|
|
116
126
|
```
|
|
117
127
|
|
|
118
128
|
### 📢 Otros Comandos
|
|
119
129
|
|
|
120
130
|
```javascript
|
|
121
131
|
// Ejecutar comando remoto (Ej: Anuncio)
|
|
122
|
-
await
|
|
132
|
+
await client.runCommand(":h ¡Hola desde la API!");
|
|
123
133
|
|
|
124
134
|
// Resetear Global Key (Solo si tienes una configurada)
|
|
125
135
|
// await erlc.resetGlobalKey();
|
|
126
136
|
```
|
|
127
137
|
|
|
138
|
+
### Uso con Token Manual
|
|
139
|
+
|
|
140
|
+
También puedes seguir pasando la server key directamente si prefieres el estilo anterior:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const server = await erlc.getServer("tu-server-key-aqui");
|
|
144
|
+
const players = await erlc.getPlayers("tu-server-key-aqui");
|
|
145
|
+
```
|
|
146
|
+
|
|
128
147
|
---
|
|
129
148
|
|
|
130
149
|
## 🚨 Manejo de Errores
|
|
@@ -133,7 +152,8 @@ La librería lanza errores descriptivos (`ErlcError`) que facilitan la depuraci
|
|
|
133
152
|
|
|
134
153
|
```javascript
|
|
135
154
|
try {
|
|
136
|
-
|
|
155
|
+
const client = new erlc.Client({ serverToken: "token-invalido" });
|
|
156
|
+
await client.connect();
|
|
137
157
|
} catch (error) {
|
|
138
158
|
console.error(`Error ${error.code}: ${error.message}`);
|
|
139
159
|
|
package/package.json
CHANGED
package/src/classes/client.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
const erlc = require("../erlc.js");
|
|
2
|
-
const
|
|
2
|
+
const getBans = require("../functions/server/getBans.js");
|
|
3
|
+
const getCommandLogs = require("../functions/server/getCommandLogs.js");
|
|
4
|
+
const getEmergencyCalls = require("../functions/server/getEmergencyCalls.js");
|
|
5
|
+
const getJoinLogs = require("../functions/server/getJoinLogs.js");
|
|
6
|
+
const getKillLogs = require("../functions/server/getKillLogs.js");
|
|
7
|
+
const getModcallLogs = require("../functions/server/getModcallLogs.js");
|
|
8
|
+
const getPlayers = require("../functions/server/getPlayers.js");
|
|
9
|
+
const getQueue = require("../functions/server/getQueue.js");
|
|
10
|
+
const getServer = require("../functions/server/getServer.js");
|
|
11
|
+
const getStaff = require("../functions/server/getStaff.js");
|
|
12
|
+
const getVehicles = require("../functions/server/getVehicles.js");
|
|
13
|
+
const runCommand = require("../functions/server/runCommand.js");
|
|
14
|
+
const { requestServer } = require("../functions/server/requestServer.js");
|
|
3
15
|
|
|
4
16
|
/**
|
|
5
17
|
* @typedef {Object} ClientConfig
|
|
6
18
|
* @property {string} [globalToken] - Your ER:LC global API token
|
|
19
|
+
* @property {string} [serverToken] - Your ER:LC private server API token
|
|
20
|
+
* @property {boolean} [validateServerToken] - Validate the server token on init
|
|
7
21
|
* @property {Object} [cache] - Cache configuration
|
|
8
22
|
* @property {boolean} [cache.enabled] - Enable in-memory cache
|
|
9
23
|
* @property {Object.<string, number>} [cache.ttlMs] - Per-endpoint TTL in ms
|
|
@@ -29,7 +43,12 @@ class Client {
|
|
|
29
43
|
} else {
|
|
30
44
|
this.options = {};
|
|
31
45
|
}
|
|
46
|
+
this.connected = false;
|
|
47
|
+
this.connectionError = null;
|
|
32
48
|
this.config();
|
|
49
|
+
this.ready = this.options.serverToken && this.options.validateServerToken !== false
|
|
50
|
+
? this.connect({ throwOnError: false })
|
|
51
|
+
: Promise.resolve(this);
|
|
33
52
|
}
|
|
34
53
|
|
|
35
54
|
/**
|
|
@@ -38,13 +57,96 @@ class Client {
|
|
|
38
57
|
*/
|
|
39
58
|
config() {
|
|
40
59
|
// Mutate existing config object to preserve references
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
if (Object.prototype.hasOwnProperty.call(this.options, "globalToken")) {
|
|
61
|
+
erlc.config.globalToken = this.options.globalToken;
|
|
62
|
+
}
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(this.options, "serverToken")) {
|
|
64
|
+
erlc.config.serverToken = this.options.serverToken;
|
|
65
|
+
}
|
|
66
|
+
if (Object.prototype.hasOwnProperty.call(this.options, "cache")) {
|
|
67
|
+
erlc.config.cache = this.options.cache;
|
|
68
|
+
}
|
|
69
|
+
if (Object.prototype.hasOwnProperty.call(this.options, "logger")) {
|
|
70
|
+
erlc.config.logger = this.options.logger;
|
|
71
|
+
}
|
|
72
|
+
if (Object.prototype.hasOwnProperty.call(this.options, "fetch")) {
|
|
73
|
+
erlc.config.fetch = this.options.fetch;
|
|
74
|
+
}
|
|
46
75
|
return erlc.config;
|
|
47
76
|
}
|
|
77
|
+
|
|
78
|
+
async connect(options = {}) {
|
|
79
|
+
const { throwOnError = true } = options;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
await requestServer(this.options.serverToken, {
|
|
83
|
+
endpoint: "server",
|
|
84
|
+
useCache: false,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
this.connected = true;
|
|
88
|
+
this.connectionError = null;
|
|
89
|
+
erlc.config.logger?.info?.("ER:LC client connected to server.");
|
|
90
|
+
} catch (error) {
|
|
91
|
+
this.connected = false;
|
|
92
|
+
this.connectionError = error;
|
|
93
|
+
erlc.config.logger?.error?.("ER:LC client failed to connect:", error);
|
|
94
|
+
|
|
95
|
+
if (throwOnError) {
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getServer(options = {}) {
|
|
104
|
+
return getServer(this.options.serverToken, options);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getPlayers() {
|
|
108
|
+
return getPlayers(this.options.serverToken);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getVehicles() {
|
|
112
|
+
return getVehicles(this.options.serverToken);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getEmergencyCalls() {
|
|
116
|
+
return getEmergencyCalls(this.options.serverToken);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getJoinLogs() {
|
|
120
|
+
return getJoinLogs(this.options.serverToken);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getKillLogs() {
|
|
124
|
+
return getKillLogs(this.options.serverToken);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getCommandLogs() {
|
|
128
|
+
return getCommandLogs(this.options.serverToken);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getModcallLogs() {
|
|
132
|
+
return getModcallLogs(this.options.serverToken);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getBans() {
|
|
136
|
+
return getBans(this.options.serverToken);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getStaff() {
|
|
140
|
+
return getStaff(this.options.serverToken);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getQueue() {
|
|
144
|
+
return getQueue(this.options.serverToken);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
runCommand(command) {
|
|
148
|
+
return runCommand(this.options.serverToken, command);
|
|
149
|
+
}
|
|
48
150
|
}
|
|
49
151
|
|
|
50
152
|
module.exports = Client;
|
|
@@ -37,6 +37,11 @@ function normalizeIncludes(options = {}) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
module.exports = (serverToken, options = {}) => {
|
|
40
|
+
if (serverToken && typeof serverToken === "object") {
|
|
41
|
+
options = serverToken;
|
|
42
|
+
serverToken = undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
const includes = normalizeIncludes(options);
|
|
41
46
|
|
|
42
47
|
return requestServer(serverToken, {
|
|
@@ -8,6 +8,12 @@ function assertServerToken(serverToken) {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
function resolveServerToken(serverToken, config) {
|
|
12
|
+
const resolvedToken = serverToken || config?.serverToken;
|
|
13
|
+
assertServerToken(resolvedToken);
|
|
14
|
+
return resolvedToken;
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
function buildHeaders(serverToken, config, extraHeaders = {}) {
|
|
12
18
|
const headers = {
|
|
13
19
|
"Server-Key": serverToken,
|
|
@@ -72,21 +78,21 @@ async function requestJson(url, options) {
|
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
async function requestServer(serverToken, options = {}) {
|
|
75
|
-
assertServerToken(serverToken);
|
|
76
|
-
|
|
77
81
|
const {
|
|
78
82
|
endpoint = "server",
|
|
79
83
|
includes = [],
|
|
80
84
|
defaultValue = {},
|
|
81
85
|
transform = (data) => data,
|
|
86
|
+
useCache = true,
|
|
82
87
|
} = options;
|
|
83
88
|
|
|
84
89
|
const { fetch, config } = await getFetch();
|
|
85
|
-
const
|
|
90
|
+
const resolvedToken = resolveServerToken(serverToken, config);
|
|
91
|
+
const shouldCache = useCache && !!config?.cache?.enabled;
|
|
86
92
|
const cacheExtras = includes.length ? includes.join(",") : "";
|
|
87
|
-
const key = cache.makeKey(endpoint,
|
|
93
|
+
const key = cache.makeKey(endpoint, resolvedToken, cacheExtras);
|
|
88
94
|
|
|
89
|
-
if (
|
|
95
|
+
if (shouldCache) {
|
|
90
96
|
const cached = cache.get(key);
|
|
91
97
|
if (cached) {
|
|
92
98
|
return cached;
|
|
@@ -96,14 +102,14 @@ async function requestServer(serverToken, options = {}) {
|
|
|
96
102
|
const data = await requestJson(buildServerUrl(includes), {
|
|
97
103
|
fetch,
|
|
98
104
|
init: {
|
|
99
|
-
headers: buildHeaders(
|
|
105
|
+
headers: buildHeaders(resolvedToken, config),
|
|
100
106
|
timeout: 10000,
|
|
101
107
|
},
|
|
102
108
|
});
|
|
103
109
|
|
|
104
110
|
const value = (await transform(data)) ?? defaultValue;
|
|
105
111
|
|
|
106
|
-
if (
|
|
112
|
+
if (shouldCache) {
|
|
107
113
|
const ttlMs = cache.getTTL(endpoint, config);
|
|
108
114
|
cache.set(key, value, ttlMs);
|
|
109
115
|
}
|
|
@@ -112,8 +118,6 @@ async function requestServer(serverToken, options = {}) {
|
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
async function requestApi(serverToken, path, options = {}) {
|
|
115
|
-
assertServerToken(serverToken);
|
|
116
|
-
|
|
117
121
|
const {
|
|
118
122
|
baseUrl = BASEURL,
|
|
119
123
|
method = "GET",
|
|
@@ -126,8 +130,9 @@ async function requestApi(serverToken, path, options = {}) {
|
|
|
126
130
|
} = options;
|
|
127
131
|
|
|
128
132
|
const { fetch, config } = await getFetch();
|
|
133
|
+
const resolvedToken = resolveServerToken(serverToken, config);
|
|
129
134
|
const shouldCache = method === "GET" && useCache && !!config?.cache?.enabled;
|
|
130
|
-
const key = cache.makeKey(endpoint,
|
|
135
|
+
const key = cache.makeKey(endpoint, resolvedToken);
|
|
131
136
|
|
|
132
137
|
if (shouldCache) {
|
|
133
138
|
const cached = cache.get(key);
|
|
@@ -137,7 +142,7 @@ async function requestApi(serverToken, path, options = {}) {
|
|
|
137
142
|
}
|
|
138
143
|
|
|
139
144
|
const headers = buildHeaders(
|
|
140
|
-
|
|
145
|
+
resolvedToken,
|
|
141
146
|
config,
|
|
142
147
|
body ? { "Content-Type": "application/json" } : {},
|
|
143
148
|
);
|
|
@@ -171,6 +176,7 @@ async function requestLegacyServer(serverToken, path, options = {}) {
|
|
|
171
176
|
|
|
172
177
|
module.exports = {
|
|
173
178
|
assertServerToken,
|
|
179
|
+
resolveServerToken,
|
|
174
180
|
buildHeaders,
|
|
175
181
|
getFetch,
|
|
176
182
|
requestServer,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { requestApi } = require("./requestServer.js");
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Executes a command on the server.
|
|
@@ -7,7 +7,10 @@ const { assertServerToken, requestApi } = require("./requestServer.js");
|
|
|
7
7
|
* @returns {Promise<boolean>} Promise that resolves to true if command was executed successfully
|
|
8
8
|
*/
|
|
9
9
|
module.exports = async (serverToken, command) => {
|
|
10
|
-
|
|
10
|
+
if (command === undefined) {
|
|
11
|
+
command = serverToken;
|
|
12
|
+
serverToken = undefined;
|
|
13
|
+
}
|
|
11
14
|
|
|
12
15
|
if (!command || typeof command !== "string") {
|
|
13
16
|
throw new Error("Command is required and must be a string");
|
package/src/types/index.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface ErrorInfo {
|
|
|
32
32
|
|
|
33
33
|
export interface ClientConfig {
|
|
34
34
|
globalToken?: string; // The ER:LC global API token
|
|
35
|
+
serverToken?: string; // The ER:LC private server API token
|
|
36
|
+
validateServerToken?: boolean; // Validate serverToken with /server on init
|
|
35
37
|
cache?: {
|
|
36
38
|
enabled?: boolean;
|
|
37
39
|
ttlMs?: Record<string, number>;
|
|
@@ -166,30 +168,49 @@ export interface VSMCommandBody {
|
|
|
166
168
|
command: string; // ":h Hey everyone!"
|
|
167
169
|
}
|
|
168
170
|
|
|
169
|
-
export function getBans(serverToken
|
|
170
|
-
export function getCommandLogs(serverToken
|
|
171
|
-
export function getJoinLogs(serverToken
|
|
172
|
-
export function getKillLogs(serverToken
|
|
173
|
-
export function getModcallLogs(serverToken
|
|
174
|
-
export function getEmergencyCalls(serverToken
|
|
175
|
-
export function getPlayers(serverToken
|
|
176
|
-
export function getQueue(serverToken
|
|
171
|
+
export function getBans(serverToken?: string): Promise<ServerBan>;
|
|
172
|
+
export function getCommandLogs(serverToken?: string): Promise<CommandLog[]>;
|
|
173
|
+
export function getJoinLogs(serverToken?: string): Promise<JoinLog[]>;
|
|
174
|
+
export function getKillLogs(serverToken?: string): Promise<KillLog[]>;
|
|
175
|
+
export function getModcallLogs(serverToken?: string): Promise<ModcallLog[]>;
|
|
176
|
+
export function getEmergencyCalls(serverToken?: string): Promise<EmergencyCall[]>;
|
|
177
|
+
export function getPlayers(serverToken?: string): Promise<ServerPlayer[]>;
|
|
178
|
+
export function getQueue(serverToken?: string): Promise<number[]>;
|
|
177
179
|
export function getServer(
|
|
178
|
-
serverToken
|
|
180
|
+
serverToken?: string,
|
|
179
181
|
options?: ServerIncludeOptions,
|
|
180
182
|
): Promise<ServerStatus>;
|
|
181
|
-
export function
|
|
182
|
-
export function
|
|
183
|
+
export function getServer(options?: ServerIncludeOptions): Promise<ServerStatus>;
|
|
184
|
+
export function getStaff(serverToken?: string): Promise<ServerStaff>;
|
|
185
|
+
export function getVehicles(serverToken?: string): Promise<VehiclesLog[]>;
|
|
183
186
|
export function runCommand(
|
|
184
187
|
serverToken: string,
|
|
185
188
|
command: string,
|
|
186
189
|
): Promise<boolean>;
|
|
190
|
+
export function runCommand(command: string): Promise<boolean>;
|
|
187
191
|
|
|
188
192
|
export function resetGlobalKey(): Promise<any>;
|
|
189
193
|
|
|
190
194
|
export class Client {
|
|
195
|
+
connected: boolean;
|
|
196
|
+
connectionError: Error | null;
|
|
197
|
+
ready: Promise<Client>;
|
|
198
|
+
|
|
191
199
|
constructor(options?: ClientConfig);
|
|
192
200
|
config(): ClientConfig;
|
|
201
|
+
connect(options?: { throwOnError?: boolean }): Promise<Client>;
|
|
202
|
+
getServer(options?: ServerIncludeOptions): Promise<ServerStatus>;
|
|
203
|
+
getPlayers(): Promise<ServerPlayer[]>;
|
|
204
|
+
getVehicles(): Promise<VehiclesLog[]>;
|
|
205
|
+
getEmergencyCalls(): Promise<EmergencyCall[]>;
|
|
206
|
+
getJoinLogs(): Promise<JoinLog[]>;
|
|
207
|
+
getKillLogs(): Promise<KillLog[]>;
|
|
208
|
+
getCommandLogs(): Promise<CommandLog[]>;
|
|
209
|
+
getModcallLogs(): Promise<ModcallLog[]>;
|
|
210
|
+
getBans(): Promise<ServerBan>;
|
|
211
|
+
getStaff(): Promise<ServerStaff>;
|
|
212
|
+
getQueue(): Promise<number[]>;
|
|
213
|
+
runCommand(command: string): Promise<boolean>;
|
|
193
214
|
}
|
|
194
215
|
|
|
195
216
|
export const utils: {
|
package/tests/cache.test.js
CHANGED
|
@@ -21,6 +21,8 @@ describe("Optional cache", () => {
|
|
|
21
21
|
beforeEach(() => {
|
|
22
22
|
fetchCalls = 0;
|
|
23
23
|
lastUrl = "";
|
|
24
|
+
erlc.config.serverToken = undefined;
|
|
25
|
+
erlc.config.globalToken = undefined;
|
|
24
26
|
erlc.config.cache.enabled = false;
|
|
25
27
|
erlc.config.fetch = mockFetch;
|
|
26
28
|
});
|
|
@@ -43,4 +45,57 @@ describe("Optional cache", () => {
|
|
|
43
45
|
expect(Array.isArray(res2)).toBe(true);
|
|
44
46
|
expect(fetchCalls).toBe(1);
|
|
45
47
|
});
|
|
48
|
+
|
|
49
|
+
test("Client validates server token on init", async () => {
|
|
50
|
+
let lastHeaders = {};
|
|
51
|
+
const logger = {
|
|
52
|
+
info: jest.fn(),
|
|
53
|
+
error: jest.fn(),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
erlc.config.fetch = async (url, opts) => {
|
|
57
|
+
fetchCalls += 1;
|
|
58
|
+
lastUrl = url;
|
|
59
|
+
lastHeaders = opts.headers;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
status: 200,
|
|
64
|
+
json: async () => ({ Name: "Test Server" }),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const client = new erlc.Client({
|
|
69
|
+
serverToken,
|
|
70
|
+
fetch: erlc.config.fetch,
|
|
71
|
+
logger,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(client.connected).toBe(false);
|
|
75
|
+
|
|
76
|
+
await client.ready;
|
|
77
|
+
|
|
78
|
+
expect(client.connected).toBe(true);
|
|
79
|
+
expect(client.connectionError).toBe(null);
|
|
80
|
+
expect(lastUrl).toBe("https://api.erlc.gg/v2/server");
|
|
81
|
+
expect(lastHeaders["Server-Key"]).toBe(serverToken);
|
|
82
|
+
expect(logger.info).toHaveBeenCalledWith("ER:LC client connected to server.");
|
|
83
|
+
expect(fetchCalls).toBe(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("Client stores server token for instance methods and global fallback", async () => {
|
|
87
|
+
const client = new erlc.Client({
|
|
88
|
+
serverToken,
|
|
89
|
+
fetch: mockFetch,
|
|
90
|
+
validateServerToken: false,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const clientPlayers = await client.getPlayers();
|
|
94
|
+
const fallbackPlayers = await erlc.getPlayers();
|
|
95
|
+
|
|
96
|
+
expect(Array.isArray(clientPlayers)).toBe(true);
|
|
97
|
+
expect(Array.isArray(fallbackPlayers)).toBe(true);
|
|
98
|
+
expect(fetchCalls).toBe(2);
|
|
99
|
+
expect(lastUrl).toBe("https://api.erlc.gg/v2/server?Players=true");
|
|
100
|
+
});
|
|
46
101
|
});
|