Install typescript, upgrade server to TypeScript
This commit is contained in:
parent
085fae4b22
commit
1eaca46a16
21
.eslintrc
21
.eslintrc
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"parser": "babel-eslint",
|
|
||||||
"extends": ["standard", "standard-react"],
|
|
||||||
"rules": {
|
|
||||||
"max-len": [2, 80, 4],
|
|
||||||
"jsx-quotes": ["error", "prefer-double"],
|
|
||||||
"padded-blocks": 0,
|
|
||||||
"import/first": 0,
|
|
||||||
"no-return-assign": 0,
|
|
||||||
"indent": ["error", 2, { "MemberExpression": 0, "SwitchCase": 1, "flatTernaryExpressions": true }],
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"expect": true,
|
|
||||||
"jest": true,
|
|
||||||
"jasmine": true,
|
|
||||||
"it": true,
|
|
||||||
"beforeEach": true,
|
|
||||||
"afterEach": true,
|
|
||||||
"describe": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
66
.eslintrc.yaml
Normal file
66
.eslintrc.yaml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
- plugin:react/recommended
|
||||||
|
- plugin:@typescript-eslint/eslint-recommended
|
||||||
|
- plugin:@typescript-eslint/recommended
|
||||||
|
plugins:
|
||||||
|
- import
|
||||||
|
settings:
|
||||||
|
react:
|
||||||
|
version: 'detect'
|
||||||
|
rules:
|
||||||
|
max-len:
|
||||||
|
- warn
|
||||||
|
- code: 80
|
||||||
|
ignorePattern: '^import .* from '
|
||||||
|
comma-dangle:
|
||||||
|
- warn
|
||||||
|
- arrays: always-multiline
|
||||||
|
objects: always-multiline
|
||||||
|
imports: always-multiline
|
||||||
|
exports: always-multiline
|
||||||
|
functions: always-multiline
|
||||||
|
|
||||||
|
semi:
|
||||||
|
- warn
|
||||||
|
- never
|
||||||
|
quotes:
|
||||||
|
- warn
|
||||||
|
- single
|
||||||
|
- allowTemplateLiterals: true
|
||||||
|
# interface-name-prefix:
|
||||||
|
'import/no-extraneous-dependencies': off
|
||||||
|
'@typescript-eslint/member-delimiter-style':
|
||||||
|
- warn
|
||||||
|
- multiline:
|
||||||
|
delimiter: none
|
||||||
|
singleline:
|
||||||
|
delimiter: comma
|
||||||
|
'@typescript-eslint/no-unused-vars':
|
||||||
|
- warn
|
||||||
|
- vars: all
|
||||||
|
args: none
|
||||||
|
ignoreRestSiblings: true
|
||||||
|
'@typescript-eslint/explicit-function-return-type': off
|
||||||
|
'@typescript-eslint/no-non-null-assertion': off
|
||||||
|
'@typescript-eslint/no-use-before-define': off
|
||||||
|
'@typescript-eslint/no-empty-interface': off
|
||||||
|
'@typescript-eslint/no-explicit-any':
|
||||||
|
- warn
|
||||||
|
- ignoreRestArgs: true
|
||||||
|
'@typescript-eslint/triple-slash-reference':
|
||||||
|
- warn
|
||||||
|
- path: always
|
||||||
|
overrides:
|
||||||
|
- files:
|
||||||
|
- '*.test.ts'
|
||||||
|
- '*.test.tsx'
|
||||||
|
rules:
|
||||||
|
'@typescript-eslint/no-explicit-any': off
|
||||||
|
- files:
|
||||||
|
- '*.js'
|
||||||
|
rules:
|
||||||
|
'@typescript-eslint/no-var-requires': off
|
||||||
|
env:
|
||||||
|
node: true
|
||||||
|
es6: true
|
||||||
16
jest.config.js
Normal file
16
jest.config.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
roots: [
|
||||||
|
'<rootDir>/src'
|
||||||
|
],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': 'ts-jest'
|
||||||
|
},
|
||||||
|
testRegex: '(/__tests__/.*|\\.(test|spec))\\.tsx?$',
|
||||||
|
moduleFileExtensions: [
|
||||||
|
'ts',
|
||||||
|
'tsx',
|
||||||
|
'js',
|
||||||
|
'jsx'
|
||||||
|
],
|
||||||
|
setupFiles: ['<rootDir>/jest.setup.js']
|
||||||
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
import '@babel/polyfill'
|
|
||||||
2074
package-lock.json
generated
2074
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
51
package.json
51
package.json
@ -3,27 +3,29 @@
|
|||||||
"version": "2.0.12",
|
"version": "2.0.12",
|
||||||
"description": "Group peer to peer video calls for anybody.",
|
"description": "Group peer to peer video calls for anybody.",
|
||||||
"repository": "https://github.com/jeremija/peer-calls",
|
"repository": "https://github.com/jeremija/peer-calls",
|
||||||
"main": "src/index.js",
|
"main": "lib/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"peercalls": "./src/index.js"
|
"peercalls": "./lib/index.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js",
|
"start": "node lib/index.js",
|
||||||
"start:server": "nodemon src/index.js --ignore build/ --ignore src/client",
|
"start:server": "nodemon -ignore build/ --ignore lib/client lib/index.js",
|
||||||
"start:watch": "chastifol [ npm run js:watch ] [ npm run css:watch ] [ npm run start:server ]",
|
"start:watch": "chastifol [ npm run ts:watch ] [ npm run js:watch ] [ npm run css:watch ] [ npm run start:server ]",
|
||||||
"watch": "",
|
"watch": "",
|
||||||
"prepublishOnly": "npm run build",
|
"prepublishOnly": "npm run build",
|
||||||
"build": "npm run css && npm run js",
|
"build": "npm run css && npm run js",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"js": "browserify -t babelify ./src/client/index.js -o ./build/index.js",
|
"js": "browserify -t babelify ./lib/client/index.js -o ./build/index.js",
|
||||||
"js:watch": "watchify -d -v -t babelify ./src/client/index.js -o ./build/index.js",
|
"js:watch": "watchify -d -v -t babelify ./lib/client/index.js -o ./build/index.js",
|
||||||
"css": "node-sass ./src/scss/style.scss -o ./build/",
|
"css": "node-sass ./src/scss/style.scss -o ./build/",
|
||||||
"css:watch": "npm run css && node-sass --watch ./src/scss/style.scss -o ./build/",
|
"css:watch": "npm run css && node-sass --watch ./src/scss/style.scss -o ./build/",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
"ci": "npm run lint && npm run test:coverage && npm run build"
|
"ci": "npm run lint && npm run test:coverage && npm run build",
|
||||||
|
"ts:watch": "tsc --build . --watch --preserveWatchOutput",
|
||||||
|
"ts": "tsc --build ."
|
||||||
},
|
},
|
||||||
"babel": {
|
"babel": {
|
||||||
"presets": [
|
"presets": [
|
||||||
@ -78,36 +80,41 @@
|
|||||||
"@babel/polyfill": "^7.4.4",
|
"@babel/polyfill": "^7.4.4",
|
||||||
"@babel/preset-env": "^7.5.0",
|
"@babel/preset-env": "^7.5.0",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
|
"@types/config": "0.0.36",
|
||||||
|
"@types/debug": "^4.1.5",
|
||||||
|
"@types/express": "^4.17.2",
|
||||||
|
"@types/jest": "^24.0.23",
|
||||||
|
"@types/node": "^12.12.7",
|
||||||
|
"@types/socket.io": "^2.1.4",
|
||||||
|
"@types/socket.io-client": "^1.4.32",
|
||||||
|
"@types/supertest": "^2.0.8",
|
||||||
|
"@types/underscore": "^1.9.3",
|
||||||
|
"@types/uuid": "^3.4.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^2.7.0",
|
||||||
|
"@typescript-eslint/parser": "^2.7.0",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-eslint": "^10.0.1",
|
"babel-eslint": "^10.0.1",
|
||||||
"babel-jest": "^24.8.0",
|
"babel-jest": "^24.8.0",
|
||||||
"babelify": "^10.0.0",
|
"babelify": "^10.0.0",
|
||||||
"chastifol": "^4.1.0",
|
"chastifol": "^4.1.0",
|
||||||
"eslint": "^5.10.0",
|
"eslint": "^6.6.0",
|
||||||
"eslint-config-standard": "^12.0.0",
|
"eslint-config-standard": "^12.0.0",
|
||||||
"eslint-config-standard-react": "^7.0.2",
|
"eslint-config-standard-react": "^7.0.2",
|
||||||
"eslint-plugin-import": "^2.3.0",
|
"eslint-plugin-import": "^2.18.2",
|
||||||
"eslint-plugin-node": "^8.0.0",
|
"eslint-plugin-node": "^8.0.0",
|
||||||
"eslint-plugin-promise": "^4.0.1",
|
"eslint-plugin-promise": "^4.0.1",
|
||||||
"eslint-plugin-react": "^7.0.1",
|
"eslint-plugin-react": "^7.16.0",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
|
"jest": "^24.9.0",
|
||||||
"jest-cli": "^24.8.0",
|
"jest-cli": "^24.8.0",
|
||||||
"node-sass": "^4.13.0",
|
"node-sass": "^4.13.0",
|
||||||
"nodemon": "^1.18.8",
|
"nodemon": "^1.18.8",
|
||||||
"redux-mock-store": "^1.2.3",
|
"redux-mock-store": "^1.2.3",
|
||||||
"supertest": "^3.0.0",
|
"supertest": "^3.0.0",
|
||||||
|
"ts-jest": "^24.1.0",
|
||||||
|
"ts-node": "^8.5.0",
|
||||||
|
"typescript": "^3.6.4",
|
||||||
"uglify-js": "^3.4.9",
|
"uglify-js": "^3.4.9",
|
||||||
"watchify": "^3.11.1"
|
"watchify": "^3.11.1"
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.[t|j]sx?$": "<rootDir>/node_modules/babel-jest"
|
|
||||||
},
|
|
||||||
"setupFiles": [
|
|
||||||
"<rootDir>/jest.setup.js"
|
|
||||||
],
|
|
||||||
"modulePathIgnorePatterns": [
|
|
||||||
"<rootDir>/node_modules/"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,10 @@ if (!process.env.DEBUG) {
|
|||||||
process.env.DEBUG = 'peercalls'
|
process.env.DEBUG = 'peercalls'
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = require('./server/app.js')
|
import app from './server/app'
|
||||||
const debug = require('debug')('peercalls')
|
import _debug from 'debug'
|
||||||
|
|
||||||
|
const debug = _debug('peercalls')
|
||||||
|
|
||||||
const port = process.env.PORT || 3000
|
const port = process.env.PORT || 3000
|
||||||
const server = app.listen(port, () => debug('Listening on: %s', port))
|
const server = app.listen(port, () => debug('Listening on: %s', port))
|
||||||
|
|||||||
@ -1,16 +1,19 @@
|
|||||||
jest.mock('socket.io', () => {
|
jest.mock('socket.io', () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const { EventEmitter } = require('events')
|
const { EventEmitter } = require('events')
|
||||||
return jest.fn().mockReturnValue(new EventEmitter())
|
return jest.fn().mockReturnValue(new EventEmitter())
|
||||||
})
|
})
|
||||||
jest.mock('./socket.js')
|
jest.mock('./socket')
|
||||||
|
|
||||||
const app = require('./app.js')
|
import app from './app'
|
||||||
const config = require('config')
|
import { config } from './config'
|
||||||
const handleSocket = require('./socket.js')
|
import handleSocket from './socket'
|
||||||
const io = require('socket.io')()
|
import SocketIO from 'socket.io'
|
||||||
const request = require('supertest')
|
import request from 'supertest'
|
||||||
|
|
||||||
const BASE_URL = config.get('baseUrl')
|
const io = SocketIO()
|
||||||
|
|
||||||
|
const BASE_URL: string = config.get('baseUrl')
|
||||||
|
|
||||||
describe('server/app', () => {
|
describe('server/app', () => {
|
||||||
|
|
||||||
@ -50,7 +53,7 @@ describe('server/app', () => {
|
|||||||
it('calls handleSocket with socket', () => {
|
it('calls handleSocket with socket', () => {
|
||||||
const socket = { hi: 'me socket' }
|
const socket = { hi: 'me socket' }
|
||||||
io.emit('connection', socket)
|
io.emit('connection', socket)
|
||||||
expect(handleSocket.mock.calls).toEqual([[ socket, io ]])
|
expect((handleSocket as jest.Mock).mock.calls).toEqual([[ socket, io ]])
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -1,20 +1,23 @@
|
|||||||
#!/usr/bin/env node
|
import { config } from './config'
|
||||||
'use strict'
|
import _debug from 'debug'
|
||||||
const config = require('config')
|
import express from 'express'
|
||||||
const debug = require('debug')('peercalls')
|
import handleSocket from './socket'
|
||||||
const express = require('express')
|
import path from 'path'
|
||||||
const handleSocket = require('./socket.js')
|
import { createServer } from './server'
|
||||||
const path = require('path')
|
import SocketIO from 'socket.io'
|
||||||
const { createServer } = require('./server.js')
|
import call from './routes/call'
|
||||||
|
import index from './routes/index'
|
||||||
|
|
||||||
const BASE_URL = config.get('baseUrl')
|
const debug = _debug('peercalls')
|
||||||
|
|
||||||
|
const BASE_URL: string = config.get('baseUrl')
|
||||||
const SOCKET_URL = `${BASE_URL}/ws`
|
const SOCKET_URL = `${BASE_URL}/ws`
|
||||||
|
|
||||||
debug(`WebSocket URL: ${SOCKET_URL}`)
|
debug(`WebSocket URL: ${SOCKET_URL}`)
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const server = createServer(config, app)
|
const server = createServer(config, app)
|
||||||
const io = require('socket.io')(server, { path: SOCKET_URL })
|
const io = SocketIO(server, { path: SOCKET_URL })
|
||||||
|
|
||||||
app.locals.version = require('../../package.json').version
|
app.locals.version = require('../../package.json').version
|
||||||
app.locals.baseUrl = BASE_URL
|
app.locals.baseUrl = BASE_URL
|
||||||
@ -25,10 +28,10 @@ app.set('views', path.join(__dirname, '../views'))
|
|||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
router.use('/res', express.static(path.join(__dirname, '../res')))
|
router.use('/res', express.static(path.join(__dirname, '../res')))
|
||||||
router.use('/static', express.static(path.join(__dirname, '../../build')))
|
router.use('/static', express.static(path.join(__dirname, '../../build')))
|
||||||
router.use('/call', require('./routes/call.js'))
|
router.use('/call', call)
|
||||||
router.use('/', require('./routes/index.js'))
|
router.use('/', index)
|
||||||
app.use(BASE_URL, router)
|
app.use(BASE_URL, router)
|
||||||
|
|
||||||
io.on('connection', socket => handleSocket(socket, io))
|
io.on('connection', socket => handleSocket(socket, io))
|
||||||
|
|
||||||
module.exports = server
|
export default server
|
||||||
26
src/server/config.ts
Normal file
26
src/server/config.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import cfg, { IConfig } from 'config'
|
||||||
|
|
||||||
|
export type ICEServer = {
|
||||||
|
url: string
|
||||||
|
urls: string[] | string
|
||||||
|
auth: 'secret'
|
||||||
|
username: string
|
||||||
|
secret: string
|
||||||
|
} | {
|
||||||
|
url: string
|
||||||
|
urls: string[] | string
|
||||||
|
auth: undefined
|
||||||
|
username: string
|
||||||
|
credential: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
baseUrl: string
|
||||||
|
iceServers: ICEServer[]
|
||||||
|
ssl: {
|
||||||
|
cert: string
|
||||||
|
key: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = cfg as IConfig & Config
|
||||||
@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
'use strict'
|
|
||||||
const config = require('config')
|
|
||||||
const turn = require('../turn.js')
|
|
||||||
const router = require('express').Router()
|
|
||||||
const uuid = require('uuid')
|
|
||||||
|
|
||||||
const BASE_URL = config.get('baseUrl')
|
|
||||||
const cfgIceServers = config.get('iceServers')
|
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
|
||||||
res.redirect(`${BASE_URL}/call/${uuid.v4()}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
router.get('/:callId', (req, res) => {
|
|
||||||
const iceServers = turn.processServers(cfgIceServers)
|
|
||||||
res.render('call', {
|
|
||||||
callId: encodeURIComponent(req.params.callId),
|
|
||||||
iceServers
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = router
|
|
||||||
23
src/server/routes/call.ts
Normal file
23
src/server/routes/call.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { config, ICEServer } from '../config'
|
||||||
|
import * as turn from '../turn'
|
||||||
|
import { Router } from 'express'
|
||||||
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
const BASE_URL: string = config.get('baseUrl')
|
||||||
|
const cfgIceServers = config.get('iceServers') as ICEServer[]
|
||||||
|
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
res.redirect(`${BASE_URL}/call/${v4()}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/:callId', (req, res) => {
|
||||||
|
const iceServers = turn.processServers(cfgIceServers)
|
||||||
|
res.render('call', {
|
||||||
|
callId: encodeURIComponent(req.params.callId),
|
||||||
|
iceServers,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
'use strict'
|
|
||||||
const router = require('express').Router()
|
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
|
||||||
res.render('index')
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = router
|
|
||||||
9
src/server/routes/index.ts
Normal file
9
src/server/routes/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Router } from 'express'
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
res.render('index')
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
@ -1,17 +0,0 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
|
|
||||||
const projectRoot = path.resolve(path.join(__dirname, '../..'))
|
|
||||||
|
|
||||||
const readFile = file => fs.readFileSync(path.resolve(projectRoot, file))
|
|
||||||
|
|
||||||
function createServer (config, app) {
|
|
||||||
if (config.ssl) {
|
|
||||||
const key = readFile(config.ssl.key)
|
|
||||||
const cert = readFile(config.ssl.cert)
|
|
||||||
return require('https').createServer({ key, cert }, app)
|
|
||||||
}
|
|
||||||
return require('http').createServer(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { createServer }
|
|
||||||
@ -1,11 +1,11 @@
|
|||||||
const express = require('express')
|
import express from 'express'
|
||||||
const http = require('http')
|
import http from 'http'
|
||||||
const https = require('https')
|
import https from 'https'
|
||||||
const { createServer } = require('./server.js')
|
import { createServer } from './server'
|
||||||
|
|
||||||
describe('server', () => {
|
describe('server', () => {
|
||||||
|
|
||||||
let app, config
|
let app: Express.Application, config: any
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = {}
|
config = {}
|
||||||
app = express()
|
app = express()
|
||||||
@ -15,7 +15,7 @@ describe('server', () => {
|
|||||||
it('creates https server when config.ssl', () => {
|
it('creates https server when config.ssl', () => {
|
||||||
config.ssl = {
|
config.ssl = {
|
||||||
cert: 'config/cert.example.pem',
|
cert: 'config/cert.example.pem',
|
||||||
key: 'config/cert.example.key'
|
key: 'config/cert.example.key',
|
||||||
}
|
}
|
||||||
const s = createServer(config, app)
|
const s = createServer(config, app)
|
||||||
expect(s).toEqual(jasmine.any(https.Server))
|
expect(s).toEqual(jasmine.any(https.Server))
|
||||||
16
src/server/server.ts
Normal file
16
src/server/server.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { readFileSync } from 'fs'
|
||||||
|
import { resolve, join } from 'path'
|
||||||
|
import { Config } from './config'
|
||||||
|
|
||||||
|
const projectRoot = resolve(join(__dirname, '../..'))
|
||||||
|
|
||||||
|
const readFile = (file: string) => readFileSync(resolve(projectRoot, file))
|
||||||
|
|
||||||
|
export function createServer (config: Config, app: Express.Application) {
|
||||||
|
if (config.ssl) {
|
||||||
|
const key = readFile(config.ssl.key)
|
||||||
|
const cert = readFile(config.ssl.cert)
|
||||||
|
return require('https').createServer({ key, cert }, app)
|
||||||
|
}
|
||||||
|
return require('http').createServer(app)
|
||||||
|
}
|
||||||
@ -1,21 +1,33 @@
|
|||||||
'use strict'
|
import { EventEmitter } from 'events'
|
||||||
|
import handleSocket from './socket'
|
||||||
const EventEmitter = require('events').EventEmitter
|
import { Socket, Server } from 'socket.io'
|
||||||
const handleSocket = require('./socket.js')
|
|
||||||
|
|
||||||
describe('server/socket', () => {
|
describe('server/socket', () => {
|
||||||
let socket, io, rooms
|
type SocketMock = Socket & {
|
||||||
|
id: string
|
||||||
|
room?: string
|
||||||
|
join: jest.Mock
|
||||||
|
leave: jest.Mock
|
||||||
|
emit: jest.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
let socket: SocketMock
|
||||||
|
let io: Server & {
|
||||||
|
in: jest.Mock<(room: string) => SocketMock>
|
||||||
|
to: jest.Mock<(room: string) => SocketMock>
|
||||||
|
}
|
||||||
|
let rooms: Record<string, {emit: any}>
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
socket = new EventEmitter()
|
socket = new EventEmitter() as SocketMock
|
||||||
socket.id = 'socket0'
|
socket.id = 'socket0'
|
||||||
socket.join = jest.fn()
|
socket.join = jest.fn()
|
||||||
socket.leave = jest.fn()
|
socket.leave = jest.fn()
|
||||||
rooms = {}
|
rooms = {}
|
||||||
|
|
||||||
io = {}
|
io = {} as any
|
||||||
io.in = io.to = jest.fn().mockImplementation(room => {
|
io.in = io.to = jest.fn().mockImplementation(room => {
|
||||||
return (rooms[room] = rooms[room] || {
|
return (rooms[room] = rooms[room] || {
|
||||||
emit: jest.fn()
|
emit: jest.fn(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -23,21 +35,21 @@ describe('server/socket', () => {
|
|||||||
adapter: {
|
adapter: {
|
||||||
rooms: {
|
rooms: {
|
||||||
room1: {
|
room1: {
|
||||||
'socket0': true
|
socket0: true,
|
||||||
},
|
} as any,
|
||||||
room2: {
|
room2: {
|
||||||
'socket0': true
|
socket0: true,
|
||||||
},
|
} as any,
|
||||||
room3: {
|
room3: {
|
||||||
sockets: {
|
sockets: {
|
||||||
'socket0': true,
|
'socket0': true,
|
||||||
'socket1': true,
|
'socket1': true,
|
||||||
'socket2': true
|
'socket2': true,
|
||||||
}
|
},
|
||||||
}
|
} as any,
|
||||||
}
|
},
|
||||||
}
|
} as any,
|
||||||
}
|
} as any
|
||||||
|
|
||||||
socket.leave = jest.fn()
|
socket.leave = jest.fn()
|
||||||
socket.join = jest.fn()
|
socket.join = jest.fn()
|
||||||
@ -52,16 +64,16 @@ describe('server/socket', () => {
|
|||||||
|
|
||||||
describe('signal', () => {
|
describe('signal', () => {
|
||||||
it('should broadcast signal to specific user', () => {
|
it('should broadcast signal to specific user', () => {
|
||||||
let signal = { type: 'signal' }
|
const signal = { type: 'signal' }
|
||||||
|
|
||||||
socket.emit('signal', { userId: 'a', signal })
|
socket.emit('signal', { userId: 'a', signal })
|
||||||
|
|
||||||
expect(io.to.mock.calls).toEqual([[ 'a' ]])
|
expect(io.to.mock.calls).toEqual([[ 'a' ]])
|
||||||
expect(io.to('a').emit.mock.calls).toEqual([[
|
expect((io.to('a').emit as jest.Mock).mock.calls).toEqual([[
|
||||||
'signal', {
|
'signal', {
|
||||||
userId: 'socket0',
|
userId: 'socket0',
|
||||||
signal
|
signal,
|
||||||
}
|
},
|
||||||
]])
|
]])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -84,19 +96,19 @@ describe('server/socket', () => {
|
|||||||
socket.emit('ready', 'room3')
|
socket.emit('ready', 'room3')
|
||||||
|
|
||||||
expect(io.to.mock.calls).toEqual([[ 'room3' ]])
|
expect(io.to.mock.calls).toEqual([[ 'room3' ]])
|
||||||
expect(io.to('room3').emit.mock.calls).toEqual([
|
expect((io.to('room3').emit as jest.Mock).mock.calls).toEqual([
|
||||||
[
|
[
|
||||||
'users', {
|
'users', {
|
||||||
initiator: 'socket0',
|
initiator: 'socket0',
|
||||||
users: [{
|
users: [{
|
||||||
id: 'socket0'
|
id: 'socket0',
|
||||||
}, {
|
}, {
|
||||||
id: 'socket1'
|
id: 'socket1',
|
||||||
}, {
|
}, {
|
||||||
id: 'socket2'
|
id: 'socket2',
|
||||||
}]
|
}],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1,13 +1,18 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
const debug = require('debug')('peer-calls:socket')
|
import _debug from 'debug'
|
||||||
const _ = require('underscore')
|
import _ from 'underscore'
|
||||||
|
import { Socket, Server } from 'socket.io'
|
||||||
|
|
||||||
module.exports = function (socket, io) {
|
const debug = _debug('peercalls:socket')
|
||||||
|
|
||||||
|
type SocketWithRoom = Socket & { room?: string }
|
||||||
|
|
||||||
|
export default function handleSocket(socket: SocketWithRoom, io: Server) {
|
||||||
socket.on('signal', payload => {
|
socket.on('signal', payload => {
|
||||||
// debug('signal: %s, payload: %o', socket.id, payload)
|
// debug('signal: %s, payload: %o', socket.id, payload)
|
||||||
io.to(payload.userId).emit('signal', {
|
io.to(payload.userId).emit('signal', {
|
||||||
userId: socket.id,
|
userId: socket.id,
|
||||||
signal: payload.signal
|
signal: payload.signal,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -18,17 +23,17 @@ module.exports = function (socket, io) {
|
|||||||
socket.join(roomName)
|
socket.join(roomName)
|
||||||
socket.room = roomName
|
socket.room = roomName
|
||||||
|
|
||||||
let users = getUsers(roomName)
|
const users = getUsers(roomName)
|
||||||
|
|
||||||
debug('ready: %s, room: %s, users: %o', socket.id, roomName, users)
|
debug('ready: %s, room: %s, users: %o', socket.id, roomName, users)
|
||||||
|
|
||||||
io.to(roomName).emit('users', {
|
io.to(roomName).emit('users', {
|
||||||
initiator: socket.id,
|
initiator: socket.id,
|
||||||
users
|
users,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function getUsers (roomName) {
|
function getUsers (roomName: string) {
|
||||||
return _.map(io.sockets.adapter.rooms[roomName].sockets, (_, id) => {
|
return _.map(io.sockets.adapter.rooms[roomName].sockets, (_, id) => {
|
||||||
return { id }
|
return { id }
|
||||||
})
|
})
|
||||||
@ -1,35 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
const crypto = require('crypto')
|
|
||||||
|
|
||||||
function getCredentials (name, secret) {
|
|
||||||
// this credential would be valid for the next 24 hours
|
|
||||||
const timestamp = parseInt(Date.now() / 1000, 10) + 24 * 3600
|
|
||||||
const username = [timestamp, name].join(':')
|
|
||||||
const hmac = crypto.createHmac('sha1', secret)
|
|
||||||
hmac.setEncoding('base64')
|
|
||||||
hmac.write(username)
|
|
||||||
hmac.end()
|
|
||||||
const credential = hmac.read()
|
|
||||||
return { username, credential }
|
|
||||||
}
|
|
||||||
|
|
||||||
function processServers (iceServers) {
|
|
||||||
return iceServers.map(server => {
|
|
||||||
switch (server.auth) {
|
|
||||||
case undefined:
|
|
||||||
return server
|
|
||||||
case 'secret':
|
|
||||||
const cred = getCredentials(server.username, server.secret)
|
|
||||||
return {
|
|
||||||
url: server.url,
|
|
||||||
urls: server.urls,
|
|
||||||
username: cred.username,
|
|
||||||
credential: cred.credential
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new Error('Authentication type not implemented: ' + server.auth)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { getCredentials, processServers }
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
const turn = require('./turn.js')
|
import * as turn from './turn'
|
||||||
|
import { ICEServer } from './config'
|
||||||
|
|
||||||
describe('server/turn', () => {
|
describe('server/turn', () => {
|
||||||
describe('getCredentials', () => {
|
describe('getCredentials', () => {
|
||||||
@ -11,17 +12,18 @@ describe('server/turn', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('processServers', () => {
|
describe('processServers', () => {
|
||||||
const servers = [{
|
const servers: ICEServer[] = [{
|
||||||
url: 'server1',
|
url: 'server1',
|
||||||
urls: 'server1',
|
urls: 'server1',
|
||||||
|
auth: undefined,
|
||||||
username: 'a',
|
username: 'a',
|
||||||
credential: 'b'
|
credential: 'b',
|
||||||
}, {
|
}, {
|
||||||
url: 'server2',
|
url: 'server2',
|
||||||
urls: 'server2',
|
urls: 'server2',
|
||||||
username: 'c',
|
username: 'c',
|
||||||
secret: 'd',
|
secret: 'd',
|
||||||
auth: 'secret'
|
auth: 'secret',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
it('does not expose secret', () => {
|
it('does not expose secret', () => {
|
||||||
@ -32,13 +34,13 @@ describe('server/turn', () => {
|
|||||||
url: 'server2',
|
url: 'server2',
|
||||||
urls: 'server2',
|
urls: 'server2',
|
||||||
username: jasmine.any(String),
|
username: jasmine.any(String),
|
||||||
credential: jasmine.any(String)
|
credential: jasmine.any(String),
|
||||||
})
|
})
|
||||||
expect(s[1].username).toMatch(/^[0-9]+:c$/)
|
expect(s[1].username).toMatch(/^[0-9]+:c$/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws error when unknown auth type', () => {
|
it('throws error when unknown auth type', () => {
|
||||||
expect(() => turn.processServers([{ auth: 'bla' }]))
|
expect(() => turn.processServers([{ auth: 'bla' } as any]))
|
||||||
.toThrowError(/not implemented/)
|
.toThrowError(/not implemented/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
45
src/server/turn.ts
Normal file
45
src/server/turn.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import crypto from 'crypto'
|
||||||
|
import { ICEServer } from './config'
|
||||||
|
|
||||||
|
export interface Credentials {
|
||||||
|
username: string
|
||||||
|
credential: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCredentials (name: string, secret: string): Credentials {
|
||||||
|
// this credential would be valid for the next 24 hours
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000) + 24 * 3600
|
||||||
|
const username = [timestamp, name].join(':')
|
||||||
|
const hmac = crypto.createHmac('sha1', secret)
|
||||||
|
hmac.setEncoding('base64')
|
||||||
|
hmac.write(username)
|
||||||
|
hmac.end()
|
||||||
|
const credential = hmac.read()
|
||||||
|
return { username, credential }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServerConfig(server: ICEServer, cred: Credentials) {
|
||||||
|
return {
|
||||||
|
url: server.url,
|
||||||
|
urls: server.urls,
|
||||||
|
username: cred.username,
|
||||||
|
credential: cred.credential,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function processServers (iceServers: ICEServer[]) {
|
||||||
|
return iceServers.map(server => {
|
||||||
|
switch (server.auth) {
|
||||||
|
case undefined:
|
||||||
|
return server
|
||||||
|
case 'secret':
|
||||||
|
return getServerConfig(
|
||||||
|
server,
|
||||||
|
getCredentials(server.username, server.secret),
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
throw new Error('Authentication type not implemented: ' +
|
||||||
|
(server as {auth: string}).auth)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"lib": ["es2015", "dom"],
|
||||||
|
"target": "es3",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"jsx": "react",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"emitDecoratorMetadata": false,
|
||||||
|
"experimentalDecorators": false,
|
||||||
|
"allowJs": false,
|
||||||
|
"checkJs": false,
|
||||||
|
"outDir": "lib",
|
||||||
|
"rootDir": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user