mirror of
https://github.com/randyjc/Rahoot.git
synced 2026-03-14 04:25:35 +01:00
adding manager UI and audio and video (youtube) questions
This commit is contained in:
@@ -7,9 +7,18 @@ import Registry from "@rahoot/socket/services/registry"
|
||||
import { withGame } from "@rahoot/socket/utils/game"
|
||||
import { Server as ServerIO } from "socket.io"
|
||||
|
||||
const corsOrigins =
|
||||
process.env.NODE_ENV !== "production"
|
||||
? "*"
|
||||
: env.WEB_ORIGIN === "*"
|
||||
? "*"
|
||||
: [env.WEB_ORIGIN, "http://localhost:3000", "http://127.0.0.1:3000"]
|
||||
|
||||
const io: Server = new ServerIO({
|
||||
cors: {
|
||||
origin: [env.WEB_ORIGIN],
|
||||
origin: corsOrigins,
|
||||
methods: ["GET", "POST"],
|
||||
credentials: false,
|
||||
},
|
||||
})
|
||||
Config.init()
|
||||
@@ -66,6 +75,42 @@ io.on("connection", (socket) => {
|
||||
}
|
||||
})
|
||||
|
||||
socket.on("manager:getQuizz", (quizzId) => {
|
||||
const quizz = Config.getQuizz(quizzId)
|
||||
|
||||
if (!quizz) {
|
||||
socket.emit("manager:errorMessage", "Quizz not found")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
socket.emit("manager:quizzLoaded", quizz)
|
||||
})
|
||||
|
||||
socket.on("manager:saveQuizz", ({ id, quizz }) => {
|
||||
if (!quizz?.subject || !Array.isArray(quizz?.questions)) {
|
||||
socket.emit("manager:errorMessage", "Invalid quizz payload")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const saved = Config.saveQuizz(id || null, quizz)
|
||||
|
||||
if (!saved) {
|
||||
socket.emit("manager:errorMessage", "Failed to save quizz")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
socket.emit("manager:quizzSaved", saved)
|
||||
socket.emit("manager:quizzList", Config.quizz())
|
||||
} catch (error) {
|
||||
console.error("Failed to save quizz", error)
|
||||
socket.emit("manager:errorMessage", "Failed to save quizz")
|
||||
}
|
||||
})
|
||||
|
||||
socket.on("game:create", (quizzId) => {
|
||||
const quizzList = Config.quizz()
|
||||
const quizz = quizzList.find((q) => q.id === quizzId)
|
||||
|
||||
@@ -2,6 +2,13 @@ import { QuizzWithId } from "@rahoot/common/types/game"
|
||||
import fs from "fs"
|
||||
import { resolve } from "path"
|
||||
|
||||
const slugify = (value: string) =>
|
||||
value
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.slice(0, 50)
|
||||
|
||||
const inContainerPath = process.env.CONFIG_PATH
|
||||
|
||||
const getPath = (path: string = "") =>
|
||||
@@ -10,13 +17,23 @@ const getPath = (path: string = "") =>
|
||||
: resolve(process.cwd(), "../../config", path)
|
||||
|
||||
class Config {
|
||||
static init() {
|
||||
static ensureBaseFolders() {
|
||||
const isConfigFolderExists = fs.existsSync(getPath())
|
||||
|
||||
if (!isConfigFolderExists) {
|
||||
fs.mkdirSync(getPath())
|
||||
}
|
||||
|
||||
const isQuizzExists = fs.existsSync(getPath("quizz"))
|
||||
|
||||
if (!isQuizzExists) {
|
||||
fs.mkdirSync(getPath("quizz"))
|
||||
}
|
||||
}
|
||||
|
||||
static init() {
|
||||
this.ensureBaseFolders()
|
||||
|
||||
const isGameConfigExists = fs.existsSync(getPath("game.json"))
|
||||
|
||||
if (!isGameConfigExists) {
|
||||
@@ -33,10 +50,10 @@ class Config {
|
||||
)
|
||||
}
|
||||
|
||||
const isQuizzExists = fs.existsSync(getPath("quizz"))
|
||||
const isQuizzExists = fs.readdirSync(getPath("quizz")).length > 0
|
||||
|
||||
if (!isQuizzExists) {
|
||||
fs.mkdirSync(getPath("quizz"))
|
||||
fs.mkdirSync(getPath("quizz"), { recursive: true })
|
||||
|
||||
fs.writeFileSync(
|
||||
getPath("quizz/example.json"),
|
||||
@@ -67,6 +84,49 @@ class Config {
|
||||
cooldown: 5,
|
||||
time: 20,
|
||||
},
|
||||
{
|
||||
question: "Which soundtrack is this?",
|
||||
answers: [
|
||||
"Nature sounds",
|
||||
"Piano solo",
|
||||
"Electronic beat",
|
||||
"Chill guitar",
|
||||
],
|
||||
media: {
|
||||
type: "audio",
|
||||
url: "https://upload.wikimedia.org/wikipedia/commons/transcoded/4/4c/Beethoven_Moonlight_1st_movement.ogg/Beethoven_Moonlight_1st_movement.ogg.mp3",
|
||||
},
|
||||
solution: 1,
|
||||
cooldown: 5,
|
||||
time: 25,
|
||||
},
|
||||
{
|
||||
question: "Which landmark appears in this clip?",
|
||||
answers: [
|
||||
"Eiffel Tower",
|
||||
"Sydney Opera House",
|
||||
"Statue of Liberty",
|
||||
"Golden Gate Bridge",
|
||||
],
|
||||
media: {
|
||||
type: "youtube",
|
||||
url: "https://www.youtube.com/watch?v=jNQXAC9IVRw",
|
||||
},
|
||||
solution: 3,
|
||||
cooldown: 5,
|
||||
time: 60,
|
||||
},
|
||||
{
|
||||
question: "What kind of animal is featured here?",
|
||||
answers: ["Dolphin", "Panda", "Horse", "Penguin"],
|
||||
media: {
|
||||
type: "youtube",
|
||||
url: "https://www.youtube.com/watch?v=2k1qW3D0q6c",
|
||||
},
|
||||
solution: 2,
|
||||
cooldown: 5,
|
||||
time: 40,
|
||||
},
|
||||
],
|
||||
},
|
||||
null,
|
||||
@@ -95,35 +155,66 @@ class Config {
|
||||
}
|
||||
|
||||
static quizz() {
|
||||
const isExists = fs.existsSync(getPath("quizz"))
|
||||
this.ensureBaseFolders()
|
||||
|
||||
if (!isExists) {
|
||||
return []
|
||||
const files = fs
|
||||
.readdirSync(getPath("quizz"))
|
||||
.filter((file) => file.endsWith(".json"))
|
||||
|
||||
const quizz: QuizzWithId[] = files.map((file) => {
|
||||
const data = fs.readFileSync(getPath(`quizz/${file}`), "utf-8")
|
||||
const config = JSON.parse(data)
|
||||
|
||||
const id = file.replace(".json", "")
|
||||
|
||||
return {
|
||||
id,
|
||||
...config,
|
||||
}
|
||||
})
|
||||
|
||||
return quizz || []
|
||||
}
|
||||
|
||||
static getQuizz(id: string) {
|
||||
this.ensureBaseFolders()
|
||||
|
||||
const filePath = getPath(`quizz/${id}.json`)
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const files = fs
|
||||
.readdirSync(getPath("quizz"))
|
||||
.filter((file) => file.endsWith(".json"))
|
||||
const data = fs.readFileSync(filePath, "utf-8")
|
||||
|
||||
const quizz: QuizzWithId[] = files.map((file) => {
|
||||
const data = fs.readFileSync(getPath(`quizz/${file}`), "utf-8")
|
||||
const config = JSON.parse(data)
|
||||
return { id, ...JSON.parse(data) } as QuizzWithId
|
||||
}
|
||||
|
||||
const id = file.replace(".json", "")
|
||||
static saveQuizz(
|
||||
id: string | null,
|
||||
quizz: QuizzWithId | Omit<QuizzWithId, "id">
|
||||
) {
|
||||
this.ensureBaseFolders()
|
||||
|
||||
return {
|
||||
id,
|
||||
...config,
|
||||
}
|
||||
})
|
||||
const slug = id
|
||||
? slugify(id)
|
||||
: slugify((quizz as any).subject || "quizz")
|
||||
const finalId = slug.length > 0 ? slug : `quizz-${Date.now()}`
|
||||
const filePath = getPath(`quizz/${finalId}.json`)
|
||||
|
||||
return quizz || []
|
||||
} catch (error) {
|
||||
console.error("Failed to read quizz config:", error)
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
subject: quizz.subject,
|
||||
questions: quizz.questions,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
|
||||
return []
|
||||
}
|
||||
return this.getQuizz(finalId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -346,6 +346,7 @@ class Game {
|
||||
this.broadcastStatus(STATUS.SHOW_QUESTION, {
|
||||
question: question.question,
|
||||
image: question.image,
|
||||
media: question.media,
|
||||
cooldown: question.cooldown,
|
||||
})
|
||||
|
||||
@@ -361,6 +362,7 @@ class Game {
|
||||
question: question.question,
|
||||
answers: question.answers,
|
||||
image: question.image,
|
||||
media: question.media,
|
||||
time: question.time,
|
||||
totalPlayer: this.players.length,
|
||||
})
|
||||
@@ -430,6 +432,7 @@ class Game {
|
||||
correct: question.solution,
|
||||
answers: question.answers,
|
||||
image: question.image,
|
||||
media: question.media,
|
||||
})
|
||||
|
||||
this.leaderboard = sortedPlayers
|
||||
|
||||
Reference in New Issue
Block a user