Compare commits
10 Commits
72d7ffd5c0
...
3b723cbad6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b723cbad6 | ||
![]() |
b050f709c9 | ||
![]() |
bea6ab8e09 | ||
![]() |
1ce0aaf927 | ||
![]() |
c8e4b690c5 | ||
![]() |
ce2c65544c | ||
![]() |
7ba8716917 | ||
![]() |
473fa4b662 | ||
![]() |
97a746c572 | ||
![]() |
0a89138a79 |
93
Jenkinsfile
vendored
93
Jenkinsfile
vendored
@ -1,47 +1,52 @@
|
||||
def label = "node-${UUID.randomUUID().toString()}"
|
||||
|
||||
podTemplate(label: label, inheritFrom: 'base', , containers: [
|
||||
containerTemplate(name: 'base', image: 'taylorchristieyardstick/build-base:node')
|
||||
]) {
|
||||
node(label) {
|
||||
stage('Checkout Project') {
|
||||
container('base') {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Install Dependencies') {
|
||||
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')]) {
|
||||
container('base') {
|
||||
sh "docker login --username ${USER} --password ${PASS}"
|
||||
}
|
||||
}
|
||||
}
|
||||
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}"
|
||||
pipeline {
|
||||
agent {
|
||||
kubernetes {
|
||||
label "kaniko-${UUID.randomUUID()}"
|
||||
inheritFrom 'kaniko'
|
||||
containerTemplate {
|
||||
name 'braid'
|
||||
image 'node:8'
|
||||
command 'sleep 9999'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
environment {
|
||||
REPOSITORY = "yardstick/braid"
|
||||
}
|
||||
stages {
|
||||
stage('Exec NPM commands') {
|
||||
steps {
|
||||
container('braid') {
|
||||
checkout scm
|
||||
sh 'npm install'
|
||||
sh 'npm install typescript'
|
||||
sh 'npx tsc'
|
||||
sh 'npm test'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build with Kaniko') {
|
||||
steps {
|
||||
withCredentials([usernamePassword(credentialsId: 'DockerHubAccessYardstick', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
|
||||
container('kaniko') {
|
||||
checkout scm
|
||||
// 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:]]*)')
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
# BRAID v1.3.0
|
||||
# BRAID v1.3.1
|
||||
> Websocket server for the Measure platform
|
||||
|
||||
[](https://semaphoreci.com/yardstick/braid)
|
||||
|
@ -1 +1 @@
|
||||
1.3.0
|
||||
1.3.1
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "braid",
|
||||
"version": "1.2.3",
|
||||
"version": "1.2.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "braid",
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import PublicChannel from './channels/types/publicChannel';
|
||||
import PrivateChannel from './channels/types/privateChannel';
|
||||
import CustomChannel from './channels/types/customChannel';
|
||||
import PublicClient from './clients/types/publicClient';
|
||||
import PrivateClient from './clients/types/privateClient';
|
||||
import CustomClient from './clients/types/customClient';
|
||||
@ -17,8 +16,8 @@ class ChannelManager {
|
||||
}
|
||||
|
||||
createChannel(data: any) {
|
||||
const channelExists: PublicChannel|PrivateChannel|CustomChannel|null = this.channelExists(data.channel);
|
||||
let channel: PublicChannel|PrivateChannel|CustomChannel;
|
||||
const channelExists: PublicChannel|PrivateChannel|null = this.channelExists(data.channel);
|
||||
let channel: PublicChannel|PrivateChannel;
|
||||
|
||||
if (channelExists) {
|
||||
channel = channelExists;
|
||||
@ -43,7 +42,7 @@ class ChannelManager {
|
||||
}
|
||||
|
||||
addClientToChannel(client: PublicClient|PrivateClient|CustomClient, channel_id: string) {
|
||||
const channel: PrivateChannel|PrivateChannel|CustomChannel|null = this.channelExists(channel_id);
|
||||
const channel: PrivateChannel|PrivateChannel|null = this.channelExists(channel_id);
|
||||
|
||||
if (channel) {
|
||||
channel.addClient(client);
|
||||
@ -54,7 +53,7 @@ class ChannelManager {
|
||||
}
|
||||
}
|
||||
|
||||
updateChannelContent(channel: PrivateChannel|PrivateChannel|CustomChannel, channelContent: JSON) {
|
||||
updateChannelContent(channel: PrivateChannel|PrivateChannel, channelContent: JSON) {
|
||||
if (channel) {
|
||||
channel.channelContent = channelContent;
|
||||
return {status: 'success'};
|
||||
@ -70,12 +69,10 @@ class ChannelManager {
|
||||
`attempting to create channel of type ${data.channel_type}, channel id: ${data.channel}...`
|
||||
);
|
||||
|
||||
if (data.channel_type === 'public') {
|
||||
return new PublicChannel(data.channel);
|
||||
} else if (data.channel_type === 'private') {
|
||||
if (data.channel_type === 'private') {
|
||||
return new PrivateChannel(data.channel);
|
||||
} else {
|
||||
return new CustomChannel(data.channel, data.custom);
|
||||
return new PublicChannel(data.channel);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
@ -1,18 +0,0 @@
|
||||
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,7 +3,6 @@ import ClientManager from '../clientManager';
|
||||
import ChannelManager from '../channelManager';
|
||||
import PublicChannel from '../channels/types/publicChannel';
|
||||
import PrivateChannel from '../channels/types/privateChannel';
|
||||
import CustomChannel from '../channels/types/customChannel';
|
||||
|
||||
const messageManager = require('../messageManager');
|
||||
const logger = require('../logger');
|
||||
@ -12,7 +11,7 @@ class ClientBase {
|
||||
ws: WebSocket;
|
||||
data: any;
|
||||
id: number;
|
||||
channel: PublicChannel|PrivateChannel|CustomChannel|null;
|
||||
channel: PublicChannel|PrivateChannel|null;
|
||||
clientManager: ClientManager;
|
||||
channelManager: ChannelManager;
|
||||
roles: string[];
|
||||
@ -68,7 +67,7 @@ class ClientBase {
|
||||
return this.data.client;
|
||||
}
|
||||
|
||||
connectToChannel(channel: PublicChannel|PrivateChannel|CustomChannel) {
|
||||
connectToChannel(channel: PublicChannel|PrivateChannel) {
|
||||
this.channel = channel;
|
||||
this.ws.on('message', this.messageListener);
|
||||
this.ws.on('close', this.closeListener);
|
||||
|
@ -16,5 +16,5 @@ module.exports = {
|
||||
algorithm: ['HS256']
|
||||
},
|
||||
messageTypes : ['broadcast', 'direct', 'changeChannel'],
|
||||
archimedesToken: process.env.ARCHIMEDES_INTEGRATION_TOKEN || 'sha1=baddbeef'
|
||||
archimedesToken: process.env.ARCHIMEDES_INTEGRATION_TOKEN || 'testkeyformeasure'
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ const AUTH_TOKEN = app.archimedesToken;
|
||||
const HTTP_PORT = app.port;
|
||||
|
||||
import { Server, IncomingMessage, ServerResponse } from 'http'
|
||||
|
||||
import * as bodyParser from "body-parser";
|
||||
interface ArchimedesMessage {
|
||||
examId: string,
|
||||
reservation_no: string
|
||||
@ -25,8 +25,12 @@ class XhrListener {
|
||||
this.#cm = cm;
|
||||
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
|
||||
app.options('*', cors());
|
||||
|
||||
@ -39,19 +43,22 @@ class XhrListener {
|
||||
app.post('/event/pause', cors(),
|
||||
(req: any, res: any, next: any) => this.eventFromArchimedes.bind(this)("pauseExam", req, res));
|
||||
|
||||
|
||||
server.on('request', app);
|
||||
|
||||
// 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}`));
|
||||
}
|
||||
|
||||
public authorized(authOffered: String): boolean {
|
||||
return AUTH_TOKEN === authOffered;
|
||||
public authorized(authOffered: String, rawBody: Buffer): boolean {
|
||||
var hash = crypto.createHmac('sha1', AUTH_TOKEN).update(rawBody).digest("hex");
|
||||
|
||||
return authOffered.split("=").reverse()[0] === hash;
|
||||
}
|
||||
|
||||
public eventFromArchimedes(eventType: string, req: any, res: any) {
|
||||
logger.debugLog.info(`eventFromArchimedes("${eventType}") called`);
|
||||
let authorized: boolean = this.authorized(req.headers['x-proctoru-signature']);
|
||||
let authorized: boolean = this.authorized(req.headers['x-proctoru-signature'], req.rawBody);
|
||||
|
||||
if (!authorized) {
|
||||
res.status(401);
|
||||
@ -74,15 +81,15 @@ class XhrListener {
|
||||
public relayEvent(event: string, archMsg: ArchimedesMessage): boolean {
|
||||
logger.debugLog.info(`XhrListener:relayEvent(event: ${event}, channel: ${archMsg.reservation_no})`);
|
||||
for (let c of this.#cm.channels) {
|
||||
if (archMsg.reservation_no === c.id) {
|
||||
if (archMsg.reservation_no == c.id) {
|
||||
let dmCount: number = 0;
|
||||
for (let client of c.clients) {
|
||||
let nonce: string = crypto.randomBytes(4).toString("hex");
|
||||
// TODO: verify the nonce against the received reply, which we currently ignore
|
||||
client.directMessage(JSON.stringify({
|
||||
client.directMessage({
|
||||
message_type: "broadcast",
|
||||
message: { event_type: event, seq_id: nonce, examId: archMsg.examId }
|
||||
}));
|
||||
});
|
||||
dmCount++;
|
||||
}
|
||||
// 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