basic MP functionality working
This commit is contained in:
@@ -21,6 +21,18 @@ services:
|
|||||||
command: server --console-address ":9001" /data
|
command: server --console-address ":9001" /data
|
||||||
volumes:
|
volumes:
|
||||||
- minio-data:/data
|
- minio-data:/data
|
||||||
|
chrome:
|
||||||
|
image: browserless/chrome:latest
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
environment:
|
||||||
|
- PREBOOT_CHROME=true
|
||||||
|
- CONNECTION_TIMEOUT=60000
|
||||||
|
- MAX_CONCURRENT_SESSIONS=10
|
||||||
|
- CHROME_REFRESH_TIME=2000
|
||||||
|
- DEBUG=*
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
minio-data:
|
minio-data:
|
||||||
|
|||||||
@@ -5,4 +5,7 @@ S3_ENDPOINT="http://localhost:9000"
|
|||||||
S3_ACCESS_KEY="minioadmin"
|
S3_ACCESS_KEY="minioadmin"
|
||||||
S3_SECRET_KEY="minioadmin"
|
S3_SECRET_KEY="minioadmin"
|
||||||
S3_BUCKET="cog-socket"
|
S3_BUCKET="cog-socket"
|
||||||
BASE_URL="http://localhost:5173"
|
BASE_URL="http://localhost:5173"
|
||||||
|
# Chrome for multiplayer functionality
|
||||||
|
CHROME_HOST="localhost"
|
||||||
|
CHROME_PORT="9222"
|
||||||
764
package-lock.json
generated
764
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@
|
|||||||
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"@types/node": "^22",
|
"@types/node": "^22",
|
||||||
|
"@types/ws": "^8.18.1",
|
||||||
"@vitest/browser": "^3.2.3",
|
"@vitest/browser": "^3.2.3",
|
||||||
"drizzle-kit": "^0.30.2",
|
"drizzle-kit": "^0.30.2",
|
||||||
"eslint": "^9.18.0",
|
"eslint": "^9.18.0",
|
||||||
@@ -68,10 +69,13 @@
|
|||||||
"drizzle-orm": "^0.40.0",
|
"drizzle-orm": "^0.40.0",
|
||||||
"lucia": "^3.2.2",
|
"lucia": "^3.2.2",
|
||||||
"mime-types": "^3.0.1",
|
"mime-types": "^3.0.1",
|
||||||
|
"morphdom": "^2.7.5",
|
||||||
"oslo": "^1.2.1",
|
"oslo": "^1.2.1",
|
||||||
"postgres": "^3.4.5",
|
"postgres": "^3.4.5",
|
||||||
|
"puppeteer-core": "^24.14.0",
|
||||||
"svelte-sonner": "^1.0.5",
|
"svelte-sonner": "^1.0.5",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
|
"ws": "^8.18.3",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ import * as schema from '$lib/server/db/schema';
|
|||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import argon2 from '@node-rs/argon2';
|
import argon2 from '@node-rs/argon2';
|
||||||
import { dev } from '$app/environment';
|
import { dev } from '$app/environment';
|
||||||
|
import { sessionManager } from '$lib/server/multiplayer/sessionManager';
|
||||||
|
|
||||||
|
// Initialize session manager (this starts the WebSocket server)
|
||||||
|
console.log('Initializing multiplayer session manager...');
|
||||||
|
// The sessionManager is initialized when imported
|
||||||
|
|
||||||
const ensureDefaultAdmin = async () => {
|
const ensureDefaultAdmin = async () => {
|
||||||
if (!dev) return;
|
if (!dev) return;
|
||||||
|
|||||||
@@ -165,7 +165,9 @@ export async function GET({ params, cookies, getClientAddress, request }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const s3Prefix = `experiments/${experimentId}/`;
|
// Files are stored in the user's experiment_files directory
|
||||||
|
const createdBy = experiment.createdBy;
|
||||||
|
const s3Prefix = `${createdBy}/${experimentId}/experiment_files/`;
|
||||||
const filePath = path === '' ? 'index.html' : path;
|
const filePath = path === '' ? 'index.html' : path;
|
||||||
const key = `${s3Prefix}${filePath}`;
|
const key = `${s3Prefix}${filePath}`;
|
||||||
|
|
||||||
@@ -199,15 +201,15 @@ export async function GET({ params, cookies, getClientAddress, request }) {
|
|||||||
// Get all files in the experiment directory to create a resource manifest
|
// Get all files in the experiment directory to create a resource manifest
|
||||||
const listCommand = new ListObjectsV2Command({
|
const listCommand = new ListObjectsV2Command({
|
||||||
Bucket: S3_BUCKET,
|
Bucket: S3_BUCKET,
|
||||||
Prefix: `experiments/${experimentId}/`
|
Prefix: s3Prefix
|
||||||
});
|
});
|
||||||
const listResponse = await s3.send(listCommand);
|
const listResponse = await s3.send(listCommand);
|
||||||
|
|
||||||
// Create resource manifest for PsychoJS
|
// Create resource manifest for PsychoJS
|
||||||
const resources = (listResponse.Contents || [])
|
const resources = (listResponse.Contents || [])
|
||||||
.filter(obj => obj.Key && obj.Key !== `experiments/${experimentId}/index.html`)
|
.filter(obj => obj.Key && obj.Key !== `${s3Prefix}index.html`)
|
||||||
.map(obj => {
|
.map(obj => {
|
||||||
const relativePath = obj.Key!.replace(`experiments/${experimentId}/`, '');
|
const relativePath = obj.Key!.replace(s3Prefix, '');
|
||||||
return {
|
return {
|
||||||
name: relativePath,
|
name: relativePath,
|
||||||
path: relativePath
|
path: relativePath
|
||||||
|
|||||||
@@ -31,6 +31,15 @@ const vendorFileRules = [
|
|||||||
export async function GET({ params, cookies, getClientAddress, request }) {
|
export async function GET({ params, cookies, getClientAddress, request }) {
|
||||||
const { experimentId, path } = params;
|
const { experimentId, path } = params;
|
||||||
|
|
||||||
|
// Check if the experiment exists
|
||||||
|
const experiment = await db.query.experiment.findFirst({
|
||||||
|
where: eq(schema.experiment.id, experimentId)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!experiment) {
|
||||||
|
throw error(404, 'Experiment not found');
|
||||||
|
}
|
||||||
|
|
||||||
// Map of requested CSS files to their location in the static directory.
|
// Map of requested CSS files to their location in the static directory.
|
||||||
const staticCssMap: Record<string, string> = {
|
const staticCssMap: Record<string, string> = {
|
||||||
'lib/vendors/survey.widgets.css': 'static/lib/psychoJS/surveyJS/survey.widgets.css',
|
'lib/vendors/survey.widgets.css': 'static/lib/psychoJS/surveyJS/survey.widgets.css',
|
||||||
@@ -151,7 +160,9 @@ export async function GET({ params, cookies, getClientAddress, request }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const s3Prefix = `experiments/${experimentId}/`;
|
// Files are stored in the user's experiment_files directory
|
||||||
|
const createdBy = experiment.createdBy;
|
||||||
|
const s3Prefix = `${createdBy}/${experimentId}/experiment_files/`;
|
||||||
const filePath = path === '' ? 'index.html' : path;
|
const filePath = path === '' ? 'index.html' : path;
|
||||||
const key = `${s3Prefix}${filePath}`;
|
const key = `${s3Prefix}${filePath}`;
|
||||||
|
|
||||||
@@ -180,20 +191,22 @@ export async function GET({ params, cookies, getClientAddress, request }) {
|
|||||||
|
|
||||||
// For PsychoJS experiments, we need to set the base href to the experiment root
|
// For PsychoJS experiments, we need to set the base href to the experiment root
|
||||||
// so that relative paths like "stimuli/image.jpg" resolve correctly
|
// so that relative paths like "stimuli/image.jpg" resolve correctly
|
||||||
const basePath = `/public/run/${experimentId}/`;
|
// Use the request URL to determine if this is multiplayer or single player
|
||||||
|
const isMultiplayer = request.url.includes('/multiplayer/');
|
||||||
|
const basePath = isMultiplayer ? `/public/multiplayer/run/${experimentId}/` : `/public/run/${experimentId}/`;
|
||||||
|
|
||||||
// Get all files in the experiment directory to create a resource manifest
|
// Get all files in the experiment directory to create a resource manifest
|
||||||
const listCommand = new ListObjectsV2Command({
|
const listCommand = new ListObjectsV2Command({
|
||||||
Bucket: S3_BUCKET,
|
Bucket: S3_BUCKET,
|
||||||
Prefix: `experiments/${experimentId}/`
|
Prefix: s3Prefix
|
||||||
});
|
});
|
||||||
const listResponse = await s3.send(listCommand);
|
const listResponse = await s3.send(listCommand);
|
||||||
|
|
||||||
// Create resource manifest for PsychoJS
|
// Create resource manifest for PsychoJS
|
||||||
const resources = (listResponse.Contents || [])
|
const resources = (listResponse.Contents || [])
|
||||||
.filter(obj => obj.Key && obj.Key !== `experiments/${experimentId}/index.html`)
|
.filter(obj => obj.Key && obj.Key !== `${s3Prefix}index.html`)
|
||||||
.map(obj => {
|
.map(obj => {
|
||||||
const relativePath = obj.Key!.replace(`experiments/${experimentId}/`, '');
|
const relativePath = obj.Key!.replace(s3Prefix, '');
|
||||||
return {
|
return {
|
||||||
name: relativePath,
|
name: relativePath,
|
||||||
path: relativePath
|
path: relativePath
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ import { defineConfig } from 'vite';
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [tailwindcss(), sveltekit(), devtoolsJson()],
|
plugins: [tailwindcss(), sveltekit(), devtoolsJson()],
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0', // Listen on all interfaces so Docker containers can access
|
||||||
|
port: 5173,
|
||||||
|
allowedHosts: ['host.docker.internal'] // Allow Docker containers to access via host.docker.internal
|
||||||
|
},
|
||||||
test: {
|
test: {
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user