Skip to content

Commit

Permalink
Handle urls better (#96)
Browse files Browse the repository at this point in the history
* sanitizeHost by casting to url

* make upgrade script

* ensure ending slash

* bump to v4.6.0

* unroll ternaries

* makeURL function

* use `makeURL` for all url needs

* remove outdated help text
  • Loading branch information
alex-Arc authored Jan 6, 2025
1 parent fbd7829 commit b0cc645
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 43 deletions.
9 changes: 4 additions & 5 deletions companion/HELP.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
## Ontime Companion Module
# Ontime Companion Module

This module gives control over Ontime leveraging its [WebSockets API](https://docs.getontime.no/api/protocols/websockets/)

### Requirements
## Requirements

- This module version requires Ontime v3 or above

### Configuration
- **Ontime server port** which is by default 4001. Keep in mind that this can be changed by the user
## Links

### Links
You can download ontime from the website [www.getontime.no](https://www.getontime.no/) \
Read the docs at [http://docs.getontime.no](https://docs.getontime.no/) \
Follow Ontime's development on [GitHub](https://github.com/cpvalente/ontime)
2 changes: 1 addition & 1 deletion companion/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "getontime-ontime",
"shortname": "ontime",
"description": "Companion module for ontime",
"version": "4.5.0",
"version": "4.6.0",
"license": "MIT",
"repository": "git+https://github.com/bitfocus/companion-module-getontime-ontime.git",
"bugs": "https://github.com/bitfocus/companion-module-getontime-ontime/issues",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "getontime-ontime",
"version": "4.5.0",
"version": "4.6.0",
"main": "/dist/index.js",
"license": "MIT",
"prettier": "@companion-module/tools/.prettierrc.json",
Expand Down
16 changes: 3 additions & 13 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SomeCompanionConfigField, Regex } from '@companion-module/base'
import { SomeCompanionConfigField } from '@companion-module/base'

export interface OntimeConfig {
host: string
port: string
port: string | null //TODO: remove
ssl: boolean
version: string //TODO: remove
refetchEvents: boolean
Expand All @@ -28,17 +28,7 @@ export function GetConfigFields(): SomeCompanionConfigField[] {
default: '127.0.0.1',
width: 6,
required: true,
tooltip: 'Ontime server address. Valid are IP or URL',
},
{
label: 'Ontime server port ',
id: 'port',
type: 'textinput',
default: '4001',
required: true,
width: 3,
regex: Regex.PORT,
tooltip: 'Ontime server port. Default is 4001',
tooltip: 'Ontime server address. eg. http://127.0.0.1:4001',
},
{
label: 'Use SSL',
Expand Down
28 changes: 27 additions & 1 deletion src/upgrades.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,30 @@ function update4xx(
return result
}

export const UpgradeScripts: CompanionStaticUpgradeScript<OntimeConfig>[] = [update2x4x0, update3x4x0, update4xx]
function update46x(_context: CompanionUpgradeContext<OntimeConfig>, props: CompanionStaticUpgradeProps<OntimeConfig>) {
const result: CompanionStaticUpgradeResult<OntimeConfig> = {
updatedConfig: null,
updatedActions: new Array<CompanionMigrationAction>(),
updatedFeedbacks: new Array<CompanionMigrationFeedback>(),
}

if (props.config === null) {
return result
}

const { host, port } = props.config
if (port === null) {
return result
}

const newAddress = `${host}:${port}`
result.updatedConfig = { ...props.config, host: newAddress, port: null }
return result
}

export const UpgradeScripts: CompanionStaticUpgradeScript<OntimeConfig>[] = [
update2x4x0,
update3x4x0,
update4xx,
update46x,
]
31 changes: 28 additions & 3 deletions src/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,34 @@ const defaultTimerObject = {
negative: '',
}

export function sanitizeHost(host: string) {
const pattern = /^((http|https):\/\/)/
return host.replace(pattern, '')
function ensureTrailingSlash(url: URL): URL {
if (!url.pathname.endsWith('/')) {
url.pathname += '/'
}
return url
}

export function makeURL(host: string, path = '', ssl = false, ws = false) {
let url: URL | undefined

if (URL.canParse(host)) {
url = new URL(host)
} else if (URL.canParse(`http://${host}`)) {
url = new URL(`http://${host}`)
}

if (url === undefined) return

url = ensureTrailingSlash(url)
url.pathname += path

if (ssl) {
url.protocol = ws ? 'wss' : 'https'
} else {
url.protocol = ws ? 'ws' : 'http'
}

return url
}

type SplitTime = typeof defaultTimerObject
Expand Down
44 changes: 25 additions & 19 deletions src/v3/connection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { InputValue, InstanceStatus } from '@companion-module/base'
import { OnTimeInstance } from '..'
import Websocket from 'ws'
import { findPreviousPlayableEvent, msToSplitTime, sanitizeHost, variablesFromCustomFields } from '../utilities'
import { findPreviousPlayableEvent, msToSplitTime, makeURL, variablesFromCustomFields } from '../utilities'
import { feedbackId, variableId } from '../enums'
import {
CurrentBlockState,
Expand All @@ -26,11 +26,10 @@ export function connect(self: OnTimeInstance, ontime: OntimeV3): void {
reconnectInterval = self.config.reconnectInterval * 1000
shouldReconnect = self.config.reconnect

const host = sanitizeHost(self.config.host)
const port = self.config.port
const wsUrls = makeURL(self.config.host, 'ws', self.config.ssl, true)

if (!host || !port) {
self.updateStatus(InstanceStatus.BadConfig, `no host and/or port defined`)
if (!wsUrls) {
self.updateStatus(InstanceStatus.BadConfig, `host format error`)
return
}

Expand All @@ -40,9 +39,9 @@ export function connect(self: OnTimeInstance, ontime: OntimeV3): void {
ws.close()
}

const prefix = self.config.ssl ? 'wss' : 'ws'
ws = new Websocket(wsUrls)

ws = new Websocket(`${prefix}://${host}:${port}/ws`)
self.log('info', `connection to server with: ${wsUrls}`)

ws.onopen = () => {
clearTimeout(reconnectionTimeout as NodeJS.Timeout)
Expand Down Expand Up @@ -352,24 +351,25 @@ export function socketSendJson(type: string, payload?: InputValue | object): voi
let rundownEtag: string = ''

async function fetchAllEvents(self: OnTimeInstance, ontime: OntimeV3): Promise<void> {
const prefix = self.config.ssl ? 'https' : 'http'
const host = sanitizeHost(self.config.host)

const serverHttp = makeURL(self.config.host, 'data/rundown', self.config.ssl)
if (!serverHttp) {
return
}
self.log('debug', 'fetching events from ontime')
try {
const response = await fetch(`${prefix}://${host}:${self.config.port}/data/rundown`, {
const response = await fetch(serverHttp.href, {
method: 'GET',
headers: { 'if-none-match': rundownEtag, 'cache-control': '3600', pragma: '' },
})
if (response.status === 304) {
self.log('debug', '304 -> nothing change in rundown')
return
}
if (!response.ok) {
ontime.events = []
self.log('error', `uable to fetch events: ${response.statusText}`)
return
}
if (response.status === 304) {
self.log('debug', '304 -> nothing change in rundown')
return
}
rundownEtag = response.headers.get('Etag') ?? ''
const data = (await response.json()) as OntimeBaseEvent[]
ontime.events = data.filter((entry) => entry.type === SupportedEvent.Event) as OntimeEvent[]
Expand All @@ -385,19 +385,25 @@ let customFieldsEtag: string = ''

//TODO: this might need to be updated on an interval
async function fetchCustomFields(self: OnTimeInstance, ontime: OntimeV3): Promise<boolean> {
const prefix = self.config.ssl ? 'https' : 'http'
const host = sanitizeHost(self.config.host)

const serverHttp = makeURL(self.config.host, 'data/custom-fields', self.config.ssl)
if (!serverHttp) {
return false
}
self.log('debug', 'fetching custom-fields from ontime')
try {
const response = await fetch(`${prefix}://${host}:${self.config.port}/data/custom-fields`, {
const response = await fetch(serverHttp, {
method: 'GET',
headers: { 'if-none-match': customFieldsEtag, 'cache-control': '3600', pragma: '' },
})
if (response.status === 304) {
self.log('debug', '304 -> nothing change custom fields')
return false
}
if (!response.ok) {
ontime.events = []
self.log('error', `uable to fetch events: ${response.statusText}`)
return false
}
customFieldsEtag = response.headers.get('Etag') ?? ''
const data = (await response.json()) as CustomFields
ontime.customFields = data
Expand Down

0 comments on commit b0cc645

Please sign in to comment.