@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/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
+ }