Skip to content

Commit

Permalink
Add initial websocket support
Browse files Browse the repository at this point in the history
  • Loading branch information
andre-dietrich committed Sep 7, 2023
1 parent cb7af00 commit 099e625
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 10 deletions.
69 changes: 59 additions & 10 deletions client/components/Modules.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default {
name: "Modules",
props: ["role", "username", "liveClassProxy"],
data() {
return {};
return { ws: null };
},
computed: {
roomName() {
Expand Down Expand Up @@ -93,16 +93,65 @@ export default {
}
},
async sendMessage(subject, body, module_url) {
if (body !== undefined)
await this.$axios.$post(
"/data/sendMessage/" + this.$store.state.class_.id,
{
from: this.username /* Email if teacher, name if station */,
subject: subject,
body: body,
module: module_url,
}
if (this.ws === null) {
const self = this;
const socket = new WebSocket(
"ws://localhost:8000/data/wss/" +
this.$store.state.class_.id +
"/" +
this.username
);
socket.onopen = function (e) {
console.warn("[open] Connection established");
self.sendMessage(subject, body, module_url);
};
const iframes = document.getElementsByTagName("iframe");
socket.onmessage = function (event) {
const data = JSON.parse(event.data);
data["event"] = "message";
for (let i = 0; i < iframes.length; i++) {
iframes[i].contentWindow.postMessage(data, "*");
}
};
socket.onclose = function (event) {
if (event.wasClean) {
console.warn(
`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`
);
} else {
console.warn("[close] Connection died");
}
};
socket.onerror = function (error) {
console.warn("error =>", error);
};
this.ws = socket;
}
if (body !== undefined) {
const data = {
from: this.username /* Email if teacher, name if station */,
subject: subject,
body: body,
module: module_url,
};
if (this.ws) {
this.ws.send(JSON.stringify(data));
} else {
await this.$axios.$post(
"/data/sendMessage/" + this.$store.state.class_.id,
data
);
}
}
},
},
};
Expand Down
85 changes: 85 additions & 0 deletions server/data_web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as env from './env.ts'
* Main in-memory data storage for all current classes on this instance
*/
const classes: data.LiveClasses = {}
const ws: Record<string, oak.WebSocket> = {}

export const router = new oak.Router()
/**
Expand Down Expand Up @@ -496,6 +497,52 @@ export const router = new oak.Router()

ctx.response.status = 200
})

.get('/wss/:class_id/:from', async (ctx) => {
const class_id = ctx?.params?.class_id
const from = ctx?.params?.from

if (!ctx.isUpgradable) {
ctx.throw(501)
}

const user_role =
classes[class_id]?.users[ctx.state.user]?.role || data.RoleName.Student

if (from) {
const socket = ctx.upgrade()

socket.onopen = () => {}

socket.onmessage = (message) => {
message = JSON.parse(message.data)

if (
!class_id ||
!data.validate_message(message, user_role)
//data.validate_email(message.from) ||
//(!data.validate_email(message.from) && user_role == 'student')
) {
return
}

broadcastMessage(class_id, message)
}

socket.onclose = () => {
console.log('Disconnected from client')
ws[from] = undefined
}

socket.onerror = (e) => {
console.log('Error from client', e)
ws[from] = undefined
}

ws[from] = socket
}
})

/**
* Broadcast message within a class room
* @param message JSON formatted: {from, subject, body}
Expand Down Expand Up @@ -606,3 +653,41 @@ function sendMessage(class_id: string, message: data.LiveMessage): boolean {

return true
}

/**
* Broadcast message within a class
* @param class_id Only this class will be updated
* @param message Message to broadcase
* @returns Success
*/
function broadcastMessage(class_id: string, message: data.LiveMessage) {
const live_class = classes[class_id]

if (!live_class) return false

/*
const info = JSON.stringify(message)
log.debug(
`Message to be sent (${class_id}) => ${
info.length > 100 ? `${info.slice(0, 100)}...` : info
}`
)
*/

/* Don't send message if not in room in class */
const user_from = live_class.users[message.from]

if (!user_from) return true

/* Send message to users within the target room */
const user_conns_in_room = Object.entries(classes[class_id]?.users || [])
.filter((u) => u[1].room == user_from.room)
.flatMap((u) => u[0])

for (const user_id of user_conns_in_room) {
if (user_id !== message.from) {
ws[user_id].send(JSON.stringify(message))
}
}
}

1 comment on commit 099e625

@deno-deploy
Copy link

@deno-deploy deno-deploy bot commented on 099e625 Sep 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed to deploy:

Module not found "file:///src/dist/app.js".

Please sign in to comment.