Compare commits
No commits in common. "3b723cbad689e0c128417192e0be57cea53f901c" and "72d7ffd5c0cd741c8fafa302c1f87a86e0d61a0d" have entirely different histories.
3b723cbad6
...
72d7ffd5c0
81
Jenkinsfile
vendored
81
Jenkinsfile
vendored
@ -1,52 +1,47 @@
|
|||||||
pipeline {
|
def label = "node-${UUID.randomUUID().toString()}"
|
||||||
agent {
|
|
||||||
kubernetes {
|
podTemplate(label: label, inheritFrom: 'base', , containers: [
|
||||||
label "kaniko-${UUID.randomUUID()}"
|
containerTemplate(name: 'base', image: 'taylorchristieyardstick/build-base:node')
|
||||||
inheritFrom 'kaniko'
|
]) {
|
||||||
containerTemplate {
|
node(label) {
|
||||||
name 'braid'
|
stage('Checkout Project') {
|
||||||
image 'node:8'
|
container('base') {
|
||||||
command 'sleep 9999'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
environment {
|
|
||||||
REPOSITORY = "yardstick/braid"
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage('Exec NPM commands') {
|
|
||||||
steps {
|
|
||||||
container('braid') {
|
|
||||||
checkout scm
|
checkout scm
|
||||||
sh 'npm install'
|
|
||||||
sh 'npm install typescript'
|
|
||||||
sh 'npx tsc'
|
|
||||||
sh 'npm test'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Build with Kaniko') {
|
stage('Install Dependencies') {
|
||||||
steps {
|
container('base') {
|
||||||
|
sh "npm install"
|
||||||
|
sh "npm install typescript"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Build the Project') {
|
||||||
|
container('base') {
|
||||||
|
sh "npx tsc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Run unit tests') {
|
||||||
|
container('base') {
|
||||||
|
sh "npm test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Login to Dockerhub') {
|
||||||
withCredentials([usernamePassword(credentialsId: 'DockerHubAccessYardstick', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
|
withCredentials([usernamePassword(credentialsId: 'DockerHubAccessYardstick', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
|
||||||
container('kaniko') {
|
container('base') {
|
||||||
checkout scm
|
sh "docker login --username ${USER} --password ${PASS}"
|
||||||
// Setup docker credentials
|
|
||||||
sh 'echo "{\\"auths\\":{\\"https://index.docker.io/v1/\\":{\\"auth\\":\\"$(printf "%s:%s" "$USER" "$PASS" | base64 | tr -d \'\n\')\\"}}}" > /kaniko/.docker/config.json'
|
|
||||||
// Execute kaniko build
|
|
||||||
sh """
|
|
||||||
/kaniko/executor -f `pwd`/Dockerfile \
|
|
||||||
-c `pwd` \
|
|
||||||
--insecure=true \
|
|
||||||
--insecure-registry=docker-registry.default:5000 \
|
|
||||||
--cache=true \
|
|
||||||
--cache-repo=docker-registry.default:5000/${env.REPOSITORY} \
|
|
||||||
--destination ${env.REPOSITORY}:\$(echo ${BRANCH_NAME} | grep -Eo 'feature/([A-Za-z]+-[0-9]*)' | grep -Eo '[A-Za-z]+-[0-9]*' || \
|
|
||||||
echo ${BRANCH_NAME} | grep -Eo '(release|hotfix)/[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+' | grep -Eo '[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+' || \
|
|
||||||
echo ${BRANCH_NAME} | grep -Eo 'YASDEV-([[:digit:]]*)')
|
|
||||||
"""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
stage('Build the Docker Image') {
|
||||||
|
container('base') {
|
||||||
|
sh "make build branch=${BRANCH_NAME}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Push the Docker Image') {
|
||||||
|
container('base') {
|
||||||
|
sh "make push branch=${BRANCH_NAME}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# BRAID v1.3.1
|
# BRAID v1.3.0
|
||||||
> Websocket server for the Measure platform
|
> Websocket server for the Measure platform
|
||||||
|
|
||||||
[](https://semaphoreci.com/yardstick/braid)
|
[](https://semaphoreci.com/yardstick/braid)
|
||||||
|
@ -1 +1 @@
|
|||||||
1.3.1
|
1.3.0
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "braid",
|
"name": "braid",
|
||||||
"version": "1.2.2",
|
"version": "1.2.3",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "braid",
|
"name": "braid",
|
||||||
"version": "1.3.1",
|
"version": "1.2.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import PublicChannel from './channels/types/publicChannel';
|
import PublicChannel from './channels/types/publicChannel';
|
||||||
import PrivateChannel from './channels/types/privateChannel';
|
import PrivateChannel from './channels/types/privateChannel';
|
||||||
|
import CustomChannel from './channels/types/customChannel';
|
||||||
import PublicClient from './clients/types/publicClient';
|
import PublicClient from './clients/types/publicClient';
|
||||||
import PrivateClient from './clients/types/privateClient';
|
import PrivateClient from './clients/types/privateClient';
|
||||||
import CustomClient from './clients/types/customClient';
|
import CustomClient from './clients/types/customClient';
|
||||||
@ -16,8 +17,8 @@ class ChannelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createChannel(data: any) {
|
createChannel(data: any) {
|
||||||
const channelExists: PublicChannel|PrivateChannel|null = this.channelExists(data.channel);
|
const channelExists: PublicChannel|PrivateChannel|CustomChannel|null = this.channelExists(data.channel);
|
||||||
let channel: PublicChannel|PrivateChannel;
|
let channel: PublicChannel|PrivateChannel|CustomChannel;
|
||||||
|
|
||||||
if (channelExists) {
|
if (channelExists) {
|
||||||
channel = channelExists;
|
channel = channelExists;
|
||||||
@ -42,7 +43,7 @@ class ChannelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addClientToChannel(client: PublicClient|PrivateClient|CustomClient, channel_id: string) {
|
addClientToChannel(client: PublicClient|PrivateClient|CustomClient, channel_id: string) {
|
||||||
const channel: PrivateChannel|PrivateChannel|null = this.channelExists(channel_id);
|
const channel: PrivateChannel|PrivateChannel|CustomChannel|null = this.channelExists(channel_id);
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
channel.addClient(client);
|
channel.addClient(client);
|
||||||
@ -53,7 +54,7 @@ class ChannelManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateChannelContent(channel: PrivateChannel|PrivateChannel, channelContent: JSON) {
|
updateChannelContent(channel: PrivateChannel|PrivateChannel|CustomChannel, channelContent: JSON) {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
channel.channelContent = channelContent;
|
channel.channelContent = channelContent;
|
||||||
return {status: 'success'};
|
return {status: 'success'};
|
||||||
@ -69,10 +70,12 @@ class ChannelManager {
|
|||||||
`attempting to create channel of type ${data.channel_type}, channel id: ${data.channel}...`
|
`attempting to create channel of type ${data.channel_type}, channel id: ${data.channel}...`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.channel_type === 'private') {
|
if (data.channel_type === 'public') {
|
||||||
|
return new PublicChannel(data.channel);
|
||||||
|
} else if (data.channel_type === 'private') {
|
||||||
return new PrivateChannel(data.channel);
|
return new PrivateChannel(data.channel);
|
||||||
} else {
|
} else {
|
||||||
return new PublicChannel(data.channel);
|
return new CustomChannel(data.channel, data.custom);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
18
src/channels/types/customChannel.ts
Normal file
18
src/channels/types/customChannel.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import CustomClient from '../../clients/types/customClient';
|
||||||
|
import ChannelBase from '../channelBase';
|
||||||
|
|
||||||
|
class CustomChannel extends ChannelBase {
|
||||||
|
clients: CustomClient[] = [];
|
||||||
|
|
||||||
|
constructor(id: string, custom: any) {
|
||||||
|
super(id);
|
||||||
|
|
||||||
|
if (custom) {
|
||||||
|
this.broadcastConditions = custom.broadcastConditions;
|
||||||
|
this.channelContent = custom.channelContent;
|
||||||
|
this.explicitRemoval = custom.explicitRemoval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomChannel;
|
@ -3,6 +3,7 @@ import ClientManager from '../clientManager';
|
|||||||
import ChannelManager from '../channelManager';
|
import ChannelManager from '../channelManager';
|
||||||
import PublicChannel from '../channels/types/publicChannel';
|
import PublicChannel from '../channels/types/publicChannel';
|
||||||
import PrivateChannel from '../channels/types/privateChannel';
|
import PrivateChannel from '../channels/types/privateChannel';
|
||||||
|
import CustomChannel from '../channels/types/customChannel';
|
||||||
|
|
||||||
const messageManager = require('../messageManager');
|
const messageManager = require('../messageManager');
|
||||||
const logger = require('../logger');
|
const logger = require('../logger');
|
||||||
@ -11,7 +12,7 @@ class ClientBase {
|
|||||||
ws: WebSocket;
|
ws: WebSocket;
|
||||||
data: any;
|
data: any;
|
||||||
id: number;
|
id: number;
|
||||||
channel: PublicChannel|PrivateChannel|null;
|
channel: PublicChannel|PrivateChannel|CustomChannel|null;
|
||||||
clientManager: ClientManager;
|
clientManager: ClientManager;
|
||||||
channelManager: ChannelManager;
|
channelManager: ChannelManager;
|
||||||
roles: string[];
|
roles: string[];
|
||||||
@ -67,7 +68,7 @@ class ClientBase {
|
|||||||
return this.data.client;
|
return this.data.client;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectToChannel(channel: PublicChannel|PrivateChannel) {
|
connectToChannel(channel: PublicChannel|PrivateChannel|CustomChannel) {
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.ws.on('message', this.messageListener);
|
this.ws.on('message', this.messageListener);
|
||||||
this.ws.on('close', this.closeListener);
|
this.ws.on('close', this.closeListener);
|
||||||
|
@ -16,5 +16,5 @@ module.exports = {
|
|||||||
algorithm: ['HS256']
|
algorithm: ['HS256']
|
||||||
},
|
},
|
||||||
messageTypes : ['broadcast', 'direct', 'changeChannel'],
|
messageTypes : ['broadcast', 'direct', 'changeChannel'],
|
||||||
archimedesToken: process.env.ARCHIMEDES_INTEGRATION_TOKEN || 'testkeyformeasure'
|
archimedesToken: process.env.ARCHIMEDES_INTEGRATION_TOKEN || 'sha1=baddbeef'
|
||||||
};
|
};
|
||||||
|
@ -11,7 +11,7 @@ const AUTH_TOKEN = app.archimedesToken;
|
|||||||
const HTTP_PORT = app.port;
|
const HTTP_PORT = app.port;
|
||||||
|
|
||||||
import { Server, IncomingMessage, ServerResponse } from 'http'
|
import { Server, IncomingMessage, ServerResponse } from 'http'
|
||||||
import * as bodyParser from "body-parser";
|
|
||||||
interface ArchimedesMessage {
|
interface ArchimedesMessage {
|
||||||
examId: string,
|
examId: string,
|
||||||
reservation_no: string
|
reservation_no: string
|
||||||
@ -25,12 +25,8 @@ class XhrListener {
|
|||||||
this.#cm = cm;
|
this.#cm = cm;
|
||||||
logger.debugLog.info("XhrListener running");
|
logger.debugLog.info("XhrListener running");
|
||||||
|
|
||||||
//app.use(jsonParser);
|
app.use(jsonParser);
|
||||||
|
|
||||||
// turns out that we must compute HMAC based on the payload of the POST body, so:
|
|
||||||
app.use(bodyParser.json({
|
|
||||||
verify: ((req: any, res: any, buf: any) => { req.rawBody = buf })
|
|
||||||
}));
|
|
||||||
// TODO: try to pin down origin to reflect client rather than use wildcard
|
// TODO: try to pin down origin to reflect client rather than use wildcard
|
||||||
app.options('*', cors());
|
app.options('*', cors());
|
||||||
|
|
||||||
@ -43,22 +39,19 @@ class XhrListener {
|
|||||||
app.post('/event/pause', cors(),
|
app.post('/event/pause', cors(),
|
||||||
(req: any, res: any, next: any) => this.eventFromArchimedes.bind(this)("pauseExam", req, res));
|
(req: any, res: any, next: any) => this.eventFromArchimedes.bind(this)("pauseExam", req, res));
|
||||||
|
|
||||||
|
|
||||||
server.on('request', app);
|
server.on('request', app);
|
||||||
|
|
||||||
// note that this kicks off HTTP listen for the entire app - not just HTTP but WS as well!
|
// note that this kicks off HTTP listen for the entire app - not just HTTP but WS as well!
|
||||||
server.listen(HTTP_PORT, () => logger.debugLog.info(`braid is now listening for HTTP and WS connections on port ${HTTP_PORT}`));
|
server.listen(HTTP_PORT, () => logger.debugLog.info(`braid is now listening for HTTP and WS connections on port ${HTTP_PORT}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
public authorized(authOffered: String, rawBody: Buffer): boolean {
|
public authorized(authOffered: String): boolean {
|
||||||
var hash = crypto.createHmac('sha1', AUTH_TOKEN).update(rawBody).digest("hex");
|
return AUTH_TOKEN === authOffered;
|
||||||
|
|
||||||
return authOffered.split("=").reverse()[0] === hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public eventFromArchimedes(eventType: string, req: any, res: any) {
|
public eventFromArchimedes(eventType: string, req: any, res: any) {
|
||||||
logger.debugLog.info(`eventFromArchimedes("${eventType}") called`);
|
logger.debugLog.info(`eventFromArchimedes("${eventType}") called`);
|
||||||
let authorized: boolean = this.authorized(req.headers['x-proctoru-signature'], req.rawBody);
|
let authorized: boolean = this.authorized(req.headers['x-proctoru-signature']);
|
||||||
|
|
||||||
if (!authorized) {
|
if (!authorized) {
|
||||||
res.status(401);
|
res.status(401);
|
||||||
@ -81,15 +74,15 @@ class XhrListener {
|
|||||||
public relayEvent(event: string, archMsg: ArchimedesMessage): boolean {
|
public relayEvent(event: string, archMsg: ArchimedesMessage): boolean {
|
||||||
logger.debugLog.info(`XhrListener:relayEvent(event: ${event}, channel: ${archMsg.reservation_no})`);
|
logger.debugLog.info(`XhrListener:relayEvent(event: ${event}, channel: ${archMsg.reservation_no})`);
|
||||||
for (let c of this.#cm.channels) {
|
for (let c of this.#cm.channels) {
|
||||||
if (archMsg.reservation_no == c.id) {
|
if (archMsg.reservation_no === c.id) {
|
||||||
let dmCount: number = 0;
|
let dmCount: number = 0;
|
||||||
for (let client of c.clients) {
|
for (let client of c.clients) {
|
||||||
let nonce: string = crypto.randomBytes(4).toString("hex");
|
let nonce: string = crypto.randomBytes(4).toString("hex");
|
||||||
// TODO: verify the nonce against the received reply, which we currently ignore
|
// TODO: verify the nonce against the received reply, which we currently ignore
|
||||||
client.directMessage({
|
client.directMessage(JSON.stringify({
|
||||||
message_type: "broadcast",
|
message_type: "broadcast",
|
||||||
message: { event_type: event, seq_id: nonce, examId: archMsg.examId }
|
message: { event_type: event, seq_id: nonce, examId: archMsg.examId }
|
||||||
});
|
}));
|
||||||
dmCount++;
|
dmCount++;
|
||||||
}
|
}
|
||||||
// dmCount of 1 would be normal. more than 1 is odd, but not necessarily bad. 0 means Exam UI has gone away somehow.
|
// dmCount of 1 would be normal. more than 1 is odd, but not necessarily bad. 0 means Exam UI has gone away somehow.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user