CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/hub/api/handler.coffee
Views: 687
1
#########################################################################
2
# This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
# License: MS-RSL – see LICENSE.md for details
4
#########################################################################
5
6
###
7
API for handling the messages described packages/util/message.js
8
9
MS-RSL, (c) 2017, SageMath, Inc.
10
###
11
12
async = require('async')
13
14
{getAccountWithApiKey} = require("@cocalc/server/api/manage");
15
16
Cache = require('lru-cache')
17
auth_cache = new Cache(max:100, ttl:60000)
18
19
misc = require('@cocalc/util/misc')
20
{defaults, required} = misc
21
22
messages = require('@cocalc/util/message')
23
24
{ HELP_EMAIL } = require("@cocalc/util/theme")
25
26
{Client} = require('../client')
27
28
log = (name, logger) ->
29
return (m) -> logger.debug("API.#{name}: #{m}")
30
31
exports.http_message_api_v1 = (opts) ->
32
try
33
opts = defaults opts,
34
event : required
35
body : required
36
api_key : required
37
database : required
38
projectControl : required
39
ip_address : required
40
logger : required
41
cb : required
42
catch err
43
opts.cb(err)
44
return
45
dbg = log('http_message_api_v1', opts.logger)
46
dbg("event=#{JSON.stringify(opts.event)}, body=#{JSON.stringify(opts.body)}")
47
48
f = messages[opts.event]
49
if not f?
50
opts.cb("unknown endpoint '#{opts.event}'")
51
return
52
53
if not messages.api_messages[opts.event]
54
opts.cb("endpoint '#{opts.event}' is not part of the HTTP API")
55
return
56
57
try
58
mesg = f(opts.body, true)
59
catch err
60
opts.cb("invalid parameters '#{err}'")
61
return
62
63
if mesg.event == 'query' and mesg.multi_response
64
otps.cb("multi_response queries aren't supported")
65
return
66
67
# client often expects id to be defined.
68
mesg.id ?= misc.uuid()
69
70
client = resp = undefined
71
async.series([
72
(cb) ->
73
get_client
74
api_key : opts.api_key
75
logger : opts.logger
76
database : opts.database
77
projectControl : opts.projectControl
78
ip_address : opts.ip_address
79
cb : (err, c) ->
80
client = c; cb(err)
81
(cb) ->
82
handle_message
83
client : client
84
mesg : mesg
85
logger : opts.logger
86
cb : (err, r) ->
87
resp = r; cb(err)
88
], (err) ->
89
if err
90
dbg("#{err} - #{JSON.stringify(resp)}")
91
opts.cb(err, resp)
92
)
93
94
get_client = (opts) ->
95
opts = defaults opts,
96
api_key : required
97
logger : required
98
database : required
99
projectControl : required
100
ip_address : required
101
cb : required
102
dbg = log('get_client', opts.logger)
103
dbg()
104
105
account_id = auth_cache.get(opts.api_key)
106
107
async.series([
108
(cb) ->
109
if account_id
110
cb()
111
else
112
try
113
account_id = await getAccountWithApiKey(opts.api_key)
114
if not account_id?
115
cb("No account found. Is your API key wrong?")
116
return
117
# briefly cache api key. see "expire" time in ms above.
118
auth_cache.set(opts.api_key, account_id)
119
cb()
120
catch err
121
cb(err)
122
return
123
(cb) ->
124
# check if user is banned:
125
opts.database.is_banned_user
126
account_id : account_id
127
cb : (err, is_banned) ->
128
if err
129
cb(err)
130
return
131
if is_banned
132
cb("User is BANNED. If this is a mistake, please contact #{HELP_EMAIL}")
133
return
134
cb()
135
136
], (err) ->
137
if err
138
opts.cb(err)
139
return
140
options =
141
logger : opts.logger
142
database : opts.database
143
projectControl : opts.projectControl
144
client = new Client(options)
145
client.push_to_client = (mesg, cb) =>
146
client.emit('push_to_client', mesg)
147
cb?()
148
client.ip_address = opts.ip_address
149
client.account_id = account_id
150
opts.cb(undefined, client)
151
)
152
153
handle_message = (opts) ->
154
opts = defaults opts,
155
mesg : required
156
client : required
157
logger : undefined
158
cb : required
159
dbg = log('handle_message', opts.logger)
160
dbg("#{JSON.stringify(opts.mesg)}, #{opts.client.id}")
161
name = "mesg_#{opts.mesg.event}"
162
f = opts.client[name]
163
if not f?
164
opts.cb("unknown message event type '#{opts.mesg.event}'")
165
return
166
opts.client.once 'push_to_client', (mesg) ->
167
opts.cb(undefined, mesg)
168
f(opts.mesg)
169
170
171
172
173