##############################################################################1#2# CoCalc: Collaborative Calculation in the Cloud3#4# Copyright (C) 2016, Sagemath Inc.5#6# This program is free software: you can redistribute it and/or modify7# it under the terms of the GNU General Public License as published by8# the Free Software Foundation, either version 3 of the License, or9# (at your option) any later version.10#11# This program is distributed in the hope that it will be useful,12# but WITHOUT ANY WARRANTY; without even the implied warranty of13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14# GNU General Public License for more details.15#16# You should have received a copy of the GNU General Public License17# along with this program. If not, see <http://www.gnu.org/licenses/>.18#19###############################################################################2021###22Periodically check the current version of the SMC code. When it changes fire a change event.2324To use, do2526smc_version = require('hub-version')27smc_version.on('change', console.log) # emited when version changes (waits CHECK_AGE_M after version time)28smc_version.version # current version29###3031CHECK_INTERVAL_S = 103233# Do not fire change event utnil this many minutes after version updated.34# We wait to give webpack (etc.) a chance to get updated.35CHECK_AGE_M = 636#CHECK_AGE_M = 03738{EventEmitter} = require('events')39path = require('path')40fs = require('fs')41winston = require('winston')42require_reload = require('require-reload')(require) # used to reload the smc-version file properly43underscore = require('underscore')44misc = require('smc-util/misc')45misc_node = require('smc-util-node/misc_node')4647# smc webapp version: when compiling with webpack, the smc-version file is read by webpack.config48# and its `version` compiled into the resulting javascript and also stored in the assets.js file.49# In the `metadata` entry, it does contain the version, build date, build timestamp, and git revision.50# Here, we are only interested in the `version`, to compare it with the version of smc-version.5152get_smc_webapp_version = (cb) ->53# with the new containerized setup, the compiled smc webapp is separated from the hub54# therefore, there is no direct file-based information about the webapp available55# TODO replace this useless callback with a http call, to retrieve the assets.json file56# History: revision 6d7dc3067c82830 shows how this has been done before that!5758# temporarily disabled59cb(null, 0); return60# TODO assets_json = retrieve base_url/assets.json61try62data = JSON.parse(assets_json)63webapp_ver = data.metadata?.version ? 064# winston.debug("get_smc_webapp_version: #{webapp_ver}")65cb(null, webapp_ver)66catch err67winston.warn("get_smc_webapp_version: error reading webapp's assets.json -- #{err}")68cb(err, 0)697071# Do a sanity check on the ver object to make sure it doesn't make it impossible72# for clients to update. (I'm just imaging future me doing some stupid editing of73# the version field by hand.)74sanity_check = (ver) ->75# ver should have fields version, min_browser_version, and min_project_version.76ver.version ?= 077ver.min_browser_version ?= 078ver.min_project_version ?= 079ver.webapp_version ?= 080# the browser can only update to the latest available code81if ver.webapp_version > 0 and ver.webapp_version < ver.min_browser_version82ver.min_browser_version = ver.webapp_version83# The min version shouldn't be bigger than the actual version84# (which is the newest the client can update to).85if ver.version < ver.min_browser_version86ver.min_browser_version = ver.version87if ver.version < ver.min_project_version88ver.min_project_version = ver.version89return ver9091class Version extends EventEmitter92constructor: (@check_interval_s=CHECK_INTERVAL_S, @check_age_m=CHECK_AGE_M) ->93# check_interval_s -- How frequently to check if the smc-util/smc-version94# module has changed.95# check_age_m -- Don't tell browser clients to upgrade until this96# many minutes after version file updated.97# initialization: short-circuit the @update check98@load_smc_version (err, smc_version) =>99if err?100winston.debug("Version.constructor err=#{err}")101@set_smc_version(smc_version)102@_check = setInterval(@update, @check_interval_s*1000)103104load_smc_version: (cb) ->105ver = require_reload('smc-util/smc-version')106get_smc_webapp_version (err, webapp_version) ->107ver.webapp_version = webapp_version108cb(err, sanity_check(ver))109110set_smc_version: (smc_version) =>111for k, v of smc_version112@[k] = v113114close: =>115if @_check?116clearInterval(@_check)117delete @_check118119update: =>120@load_smc_version (err, smc_version) =>121# winston.debug("Version.update: smc_version = #{misc.to_json(smc_version)}")122if err?123winston.debug("Version.update err=#{err}")124if not smc_version.version125# not using versions126return127ver_age_s = (new Date() - smc_version.version * 1000)/1000128# winston.debug("Version.update: ver_age_s=#{ver_age_s}, CHECK_AGE_M*60=#{CHECK_AGE_M*60}")129if ver_age_s <= @check_age_m * 60130# do nothing - we wait until the version in the file is at least SMC_VERSION_CHECK_AGE_M old131return132if not underscore.isEqual(@version, smc_version.version)133# we have a new version: updating the instance fields and emitting it to listeners134@set_smc_version(smc_version)135winston.debug("update_smc_version: update -- #{misc.to_json(smc_version)}")136@emit('change', smc_version)137138# export a single version object139module.exports = new Version()140141142