@gameap/debug 0.2.0
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 +222 -0
- package/empty-plugin/plugin.js +7 -0
- package/index.html +111 -0
- package/package.json +38 -0
- package/postcss.config.cjs +12 -0
- package/src/main.ts +328 -0
- package/src/mocks/README.md +68 -0
- package/src/mocks/browser.ts +22 -0
- package/src/mocks/files.ts +347 -0
- package/src/mocks/handlers.ts +940 -0
- package/src/mocks/servers.ts +397 -0
- package/src/mocks/translations-en.json +704 -0
- package/src/mocks/translations-ru.json +692 -0
- package/src/mocks/users.ts +34 -0
- package/src/shims-vue.d.ts +5 -0
- package/tailwind.config.js +25 -0
- package/tsconfig.json +31 -0
- package/tsconfig.node.json +11 -0
- package/vite.config.ts +93 -0
package/src/main.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GameAP Debug Harness - Main Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This harness loads the real GameAP frontend with mock API responses,
|
|
5
|
+
* allowing plugin testing in a realistic environment.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { startMockServiceWorker, setPluginContent, updateDebugState } from './mocks/browser'
|
|
9
|
+
|
|
10
|
+
// Declare window globals for plugin compatibility
|
|
11
|
+
declare global {
|
|
12
|
+
interface Window {
|
|
13
|
+
Vue: typeof import('vue')
|
|
14
|
+
VueRouter: typeof import('vue-router')
|
|
15
|
+
Pinia: typeof import('pinia')
|
|
16
|
+
axios: typeof import('axios').default
|
|
17
|
+
gameapLang: string
|
|
18
|
+
gameapDebug: {
|
|
19
|
+
updateDebugState: typeof updateDebugState
|
|
20
|
+
setPluginContent: typeof setPluginContent
|
|
21
|
+
loadPlugin: (js: string, css?: string) => void
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Load plugin from dist directory (set via PLUGIN_PATH env var)
|
|
27
|
+
// Using glob imports to handle dynamic file names
|
|
28
|
+
const pluginJsFiles = import.meta.glob('@plugin/plugin.js', { query: '?raw', import: 'default', eager: true })
|
|
29
|
+
const pluginCssFiles = import.meta.glob('@plugin/*.css', { query: '?raw', import: 'default', eager: true })
|
|
30
|
+
|
|
31
|
+
async function loadPluginBundle(): Promise<{ js: string; css: string }> {
|
|
32
|
+
try {
|
|
33
|
+
// Get plugin JS
|
|
34
|
+
const jsEntries = Object.entries(pluginJsFiles)
|
|
35
|
+
const pluginJs = jsEntries.length > 0 ? (jsEntries[0][1] as string) : ''
|
|
36
|
+
|
|
37
|
+
// Get plugin CSS (any .css file in the dist)
|
|
38
|
+
const cssEntries = Object.entries(pluginCssFiles)
|
|
39
|
+
const pluginCss = cssEntries.length > 0 ? (cssEntries[0][1] as string) : ''
|
|
40
|
+
|
|
41
|
+
if (!pluginJs) {
|
|
42
|
+
console.warn('[Debug] No plugin.js found in plugin directory')
|
|
43
|
+
}
|
|
44
|
+
if (!pluginCss) {
|
|
45
|
+
console.log('[Debug] No plugin CSS found')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { js: pluginJs, css: pluginCss }
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.warn('[Debug] Could not load plugin bundle:', error)
|
|
51
|
+
return { js: '', css: '' }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Set up mock authentication based on debug state
|
|
56
|
+
function setupMockAuth() {
|
|
57
|
+
// Get stored debug user type or default to 'admin'
|
|
58
|
+
const storedUserType = localStorage.getItem('gameap_debug_user_type') || 'admin'
|
|
59
|
+
updateDebugState({ userType: storedUserType as 'admin' | 'user' | 'guest' })
|
|
60
|
+
|
|
61
|
+
if (storedUserType === 'guest') {
|
|
62
|
+
// Guest mode - remove auth token
|
|
63
|
+
localStorage.removeItem('auth_token')
|
|
64
|
+
console.log('[Debug] Auth: Guest mode (not authenticated)')
|
|
65
|
+
} else {
|
|
66
|
+
// Authenticated mode - set mock token
|
|
67
|
+
const mockToken = 'mock-debug-token-' + storedUserType
|
|
68
|
+
localStorage.setItem('auth_token', mockToken)
|
|
69
|
+
console.log('[Debug] Auth: Authenticated as', storedUserType)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Load translations after MSW starts (since MSW intercepts the request)
|
|
74
|
+
async function loadTranslations() {
|
|
75
|
+
const lang = window.gameapLang || 'en'
|
|
76
|
+
console.log('[Debug] Loading translations for:', lang)
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(`/lang/${lang}.json`)
|
|
80
|
+
if (response.ok) {
|
|
81
|
+
window.i18n = await response.json()
|
|
82
|
+
console.log('[Debug] Translations loaded successfully')
|
|
83
|
+
} else {
|
|
84
|
+
console.warn('[Debug] Failed to load translations, using fallback')
|
|
85
|
+
window.i18n = {}
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('[Debug] Error loading translations:', error)
|
|
89
|
+
window.i18n = {}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Initialize the debug harness
|
|
94
|
+
async function init() {
|
|
95
|
+
console.log('[Debug] Starting GameAP Debug Harness...')
|
|
96
|
+
|
|
97
|
+
// Load plugin bundle first
|
|
98
|
+
console.log('[Debug] Loading plugin bundle...')
|
|
99
|
+
const { js, css } = await loadPluginBundle()
|
|
100
|
+
|
|
101
|
+
if (js) {
|
|
102
|
+
console.log('[Debug] Plugin bundle loaded, size:', js.length, 'bytes')
|
|
103
|
+
setPluginContent(js, css)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Start MSW
|
|
107
|
+
console.log('[Debug] Starting Mock Service Worker...')
|
|
108
|
+
await startMockServiceWorker()
|
|
109
|
+
console.log('[Debug] MSW started successfully')
|
|
110
|
+
|
|
111
|
+
// Set up mock authentication BEFORE loading the app
|
|
112
|
+
setupMockAuth()
|
|
113
|
+
|
|
114
|
+
// Load translations AFTER MSW starts (so MSW can intercept the request)
|
|
115
|
+
await loadTranslations()
|
|
116
|
+
|
|
117
|
+
// Expose debug utilities globally
|
|
118
|
+
window.gameapDebug = {
|
|
119
|
+
updateDebugState,
|
|
120
|
+
setPluginContent,
|
|
121
|
+
loadPlugin: (newJs: string, newCss?: string) => {
|
|
122
|
+
setPluginContent(newJs, newCss || '')
|
|
123
|
+
console.log('[Debug] Plugin content updated, reload to apply')
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Now load the real GameAP frontend
|
|
128
|
+
console.log('[Debug] Loading GameAP frontend...')
|
|
129
|
+
|
|
130
|
+
// Import the frontend from npm package - this will initialize the Vue app
|
|
131
|
+
await import('@gameap/frontend')
|
|
132
|
+
|
|
133
|
+
console.log('[Debug] GameAP frontend loaded successfully')
|
|
134
|
+
|
|
135
|
+
// Add debug panel after app is mounted
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
createDebugPanel()
|
|
138
|
+
}, 500)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Create a floating debug panel for controlling the mock environment
|
|
142
|
+
function createDebugPanel() {
|
|
143
|
+
const panel = document.createElement('div')
|
|
144
|
+
panel.id = 'gameap-debug-panel'
|
|
145
|
+
panel.innerHTML = `
|
|
146
|
+
<style>
|
|
147
|
+
#gameap-debug-panel {
|
|
148
|
+
position: fixed;
|
|
149
|
+
bottom: 20px;
|
|
150
|
+
right: 20px;
|
|
151
|
+
z-index: 99999;
|
|
152
|
+
font-family: system-ui, sans-serif;
|
|
153
|
+
font-size: 12px;
|
|
154
|
+
}
|
|
155
|
+
#gameap-debug-panel .debug-toggle {
|
|
156
|
+
background: #4f46e5;
|
|
157
|
+
color: white;
|
|
158
|
+
border: none;
|
|
159
|
+
padding: 8px 12px;
|
|
160
|
+
border-radius: 8px;
|
|
161
|
+
cursor: pointer;
|
|
162
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
gap: 6px;
|
|
166
|
+
}
|
|
167
|
+
#gameap-debug-panel .debug-toggle:hover {
|
|
168
|
+
background: #4338ca;
|
|
169
|
+
}
|
|
170
|
+
#gameap-debug-panel .debug-content {
|
|
171
|
+
display: none;
|
|
172
|
+
position: absolute;
|
|
173
|
+
bottom: 100%;
|
|
174
|
+
right: 0;
|
|
175
|
+
margin-bottom: 8px;
|
|
176
|
+
background: white;
|
|
177
|
+
border-radius: 12px;
|
|
178
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
|
179
|
+
min-width: 280px;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
#gameap-debug-panel.open .debug-content {
|
|
183
|
+
display: block;
|
|
184
|
+
}
|
|
185
|
+
#gameap-debug-panel .debug-header {
|
|
186
|
+
background: #4f46e5;
|
|
187
|
+
color: white;
|
|
188
|
+
padding: 12px 16px;
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
}
|
|
191
|
+
#gameap-debug-panel .debug-body {
|
|
192
|
+
padding: 16px;
|
|
193
|
+
}
|
|
194
|
+
#gameap-debug-panel .debug-section {
|
|
195
|
+
margin-bottom: 12px;
|
|
196
|
+
}
|
|
197
|
+
#gameap-debug-panel .debug-section:last-child {
|
|
198
|
+
margin-bottom: 0;
|
|
199
|
+
}
|
|
200
|
+
#gameap-debug-panel label {
|
|
201
|
+
display: block;
|
|
202
|
+
font-size: 11px;
|
|
203
|
+
font-weight: 500;
|
|
204
|
+
color: #64748b;
|
|
205
|
+
margin-bottom: 4px;
|
|
206
|
+
text-transform: uppercase;
|
|
207
|
+
}
|
|
208
|
+
#gameap-debug-panel select, #gameap-debug-panel input {
|
|
209
|
+
width: 100%;
|
|
210
|
+
padding: 8px 10px;
|
|
211
|
+
border: 1px solid #e2e8f0;
|
|
212
|
+
border-radius: 6px;
|
|
213
|
+
font-size: 13px;
|
|
214
|
+
}
|
|
215
|
+
#gameap-debug-panel select:focus, #gameap-debug-panel input:focus {
|
|
216
|
+
outline: none;
|
|
217
|
+
border-color: #4f46e5;
|
|
218
|
+
}
|
|
219
|
+
#gameap-debug-panel .debug-badge {
|
|
220
|
+
background: #dbeafe;
|
|
221
|
+
color: #1d4ed8;
|
|
222
|
+
padding: 4px 8px;
|
|
223
|
+
border-radius: 4px;
|
|
224
|
+
font-size: 11px;
|
|
225
|
+
margin-top: 4px;
|
|
226
|
+
display: inline-block;
|
|
227
|
+
}
|
|
228
|
+
@media (prefers-color-scheme: dark) {
|
|
229
|
+
#gameap-debug-panel .debug-content {
|
|
230
|
+
background: #1e293b;
|
|
231
|
+
}
|
|
232
|
+
#gameap-debug-panel label {
|
|
233
|
+
color: #94a3b8;
|
|
234
|
+
}
|
|
235
|
+
#gameap-debug-panel select, #gameap-debug-panel input {
|
|
236
|
+
background: #0f172a;
|
|
237
|
+
border-color: #334155;
|
|
238
|
+
color: #f1f5f9;
|
|
239
|
+
}
|
|
240
|
+
#gameap-debug-panel .debug-badge {
|
|
241
|
+
background: #1e3a5f;
|
|
242
|
+
color: #93c5fd;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
</style>
|
|
246
|
+
<div class="debug-content">
|
|
247
|
+
<div class="debug-header">🔧 Debug Panel</div>
|
|
248
|
+
<div class="debug-body">
|
|
249
|
+
<div class="debug-section">
|
|
250
|
+
<label>User Type</label>
|
|
251
|
+
<select id="debug-user-type">
|
|
252
|
+
<option value="admin">Admin</option>
|
|
253
|
+
<option value="user">Regular User</option>
|
|
254
|
+
<option value="guest">Guest (not authenticated)</option>
|
|
255
|
+
</select>
|
|
256
|
+
</div>
|
|
257
|
+
<div class="debug-section">
|
|
258
|
+
<label>Network Delay (ms)</label>
|
|
259
|
+
<input type="number" id="debug-network-delay" value="100" min="0" max="5000" step="50">
|
|
260
|
+
</div>
|
|
261
|
+
<div class="debug-section">
|
|
262
|
+
<label>Locale</label>
|
|
263
|
+
<select id="debug-locale">
|
|
264
|
+
<option value="en">English</option>
|
|
265
|
+
<option value="ru">Русский</option>
|
|
266
|
+
</select>
|
|
267
|
+
</div>
|
|
268
|
+
<div class="debug-section">
|
|
269
|
+
<span class="debug-badge">MSW Active</span>
|
|
270
|
+
<span class="debug-badge">Plugin Loaded</span>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
<button class="debug-toggle">
|
|
275
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
276
|
+
<circle cx="12" cy="12" r="3"></circle>
|
|
277
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
|
278
|
+
</svg>
|
|
279
|
+
Debug
|
|
280
|
+
</button>
|
|
281
|
+
`
|
|
282
|
+
|
|
283
|
+
document.body.appendChild(panel)
|
|
284
|
+
|
|
285
|
+
// Toggle panel
|
|
286
|
+
const toggleBtn = panel.querySelector('.debug-toggle') as HTMLButtonElement
|
|
287
|
+
toggleBtn.addEventListener('click', () => {
|
|
288
|
+
panel.classList.toggle('open')
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
// User type change
|
|
292
|
+
const userTypeSelect = panel.querySelector('#debug-user-type') as HTMLSelectElement
|
|
293
|
+
// Set current value from localStorage
|
|
294
|
+
const currentUserType = localStorage.getItem('gameap_debug_user_type') || 'admin'
|
|
295
|
+
userTypeSelect.value = currentUserType
|
|
296
|
+
|
|
297
|
+
userTypeSelect.addEventListener('change', () => {
|
|
298
|
+
const newUserType = userTypeSelect.value as 'admin' | 'user' | 'guest'
|
|
299
|
+
// Save to localStorage for persistence
|
|
300
|
+
localStorage.setItem('gameap_debug_user_type', newUserType)
|
|
301
|
+
updateDebugState({ userType: newUserType })
|
|
302
|
+
console.log('[Debug] User type changed to:', newUserType)
|
|
303
|
+
// Reload to apply
|
|
304
|
+
if (confirm('Reload page to apply user type change?')) {
|
|
305
|
+
window.location.reload()
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
// Network delay change
|
|
310
|
+
const delayInput = panel.querySelector('#debug-network-delay') as HTMLInputElement
|
|
311
|
+
delayInput.addEventListener('change', () => {
|
|
312
|
+
updateDebugState({ networkDelay: parseInt(delayInput.value) || 100 })
|
|
313
|
+
console.log('[Debug] Network delay set to:', delayInput.value, 'ms')
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
// Locale change
|
|
317
|
+
const localeSelect = panel.querySelector('#debug-locale') as HTMLSelectElement
|
|
318
|
+
localeSelect.addEventListener('change', () => {
|
|
319
|
+
updateDebugState({ locale: localeSelect.value as 'en' | 'ru' })
|
|
320
|
+
window.gameapLang = localeSelect.value
|
|
321
|
+
console.log('[Debug] Locale changed to:', localeSelect.value)
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Start initialization
|
|
326
|
+
init().catch(error => {
|
|
327
|
+
console.error('[Debug] Failed to initialize:', error)
|
|
328
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Mock API System
|
|
2
|
+
|
|
3
|
+
This directory contains a complete mock API system using [Mock Service Worker (MSW)](https://mswjs.io/) to enable plugin development and testing without a live backend server.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
| File | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| `browser.ts` | MSW worker setup and initialization |
|
|
10
|
+
| `handlers.ts` | HTTP request handlers covering 40+ API endpoints |
|
|
11
|
+
| `servers.ts` | Mock game server data, games, and server capabilities |
|
|
12
|
+
| `users.ts` | Mock user profiles (admin, regular user, guest) |
|
|
13
|
+
| `files.ts` | Mock file system with nested directories |
|
|
14
|
+
| `translations-en.json` | English language strings |
|
|
15
|
+
| `translations-ru.json` | Russian language strings |
|
|
16
|
+
|
|
17
|
+
## Debug State
|
|
18
|
+
|
|
19
|
+
The mock system exposes a configurable debug state:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
debugState = {
|
|
23
|
+
userType: 'admin' | 'user' | 'guest', // Controls permission level
|
|
24
|
+
serverId: 1 | 2 | 3, // Selected mock server
|
|
25
|
+
locale: 'en' | 'ru', // UI language
|
|
26
|
+
networkDelay: 100 // Simulated latency (ms)
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Update state via:
|
|
31
|
+
```typescript
|
|
32
|
+
import { updateDebugState } from './browser'
|
|
33
|
+
updateDebugState({ userType: 'user', networkDelay: 500 })
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Mock Servers
|
|
37
|
+
|
|
38
|
+
Three game servers with different states for testing:
|
|
39
|
+
|
|
40
|
+
1. **Minecraft Survival** (ID: 1) - Installed, online, running
|
|
41
|
+
2. **CS2 Competitive** (ID: 2) - Installed, offline, not running
|
|
42
|
+
3. **Rust Server** (ID: 3) - Not installed
|
|
43
|
+
|
|
44
|
+
Each server has different capabilities (RCON, console access, file manager, etc.) to test various plugin scenarios.
|
|
45
|
+
|
|
46
|
+
## API Coverage
|
|
47
|
+
|
|
48
|
+
- Auth & Profile
|
|
49
|
+
- Servers (list, control, console, RCON, tasks)
|
|
50
|
+
- Games & Mods
|
|
51
|
+
- Dedicated Servers / Nodes
|
|
52
|
+
- Users & Permissions
|
|
53
|
+
- Tokens & Certificates
|
|
54
|
+
- GDaemon Tasks
|
|
55
|
+
- File Manager (browse, upload, download, zip/unzip)
|
|
56
|
+
- Plugins (JS/CSS loading)
|
|
57
|
+
- Translations
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { startMockServiceWorker } from './browser'
|
|
63
|
+
|
|
64
|
+
await startMockServiceWorker()
|
|
65
|
+
// All fetch requests to /api/* are now intercepted
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The debug panel (in main.ts) provides UI controls for switching user types, adjusting network delay, and changing locale.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { setupWorker } from 'msw/browser'
|
|
2
|
+
import { handlers, debugState, setPluginContent } from './handlers'
|
|
3
|
+
|
|
4
|
+
export const worker = setupWorker(...handlers)
|
|
5
|
+
|
|
6
|
+
// Export debug state and utilities for external control
|
|
7
|
+
export { debugState, setPluginContent }
|
|
8
|
+
|
|
9
|
+
// Helper to update debug state
|
|
10
|
+
export function updateDebugState(updates: Partial<typeof debugState>) {
|
|
11
|
+
Object.assign(debugState, updates)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Start the worker
|
|
15
|
+
export async function startMockServiceWorker() {
|
|
16
|
+
return worker.start({
|
|
17
|
+
onUnhandledRequest: 'bypass', // Don't warn about unhandled requests
|
|
18
|
+
serviceWorker: {
|
|
19
|
+
url: '/mockServiceWorker.js',
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
}
|