Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

318 righe
7.4 KiB

  1. const cookie = require('cookie');
  2. const request = require('superagent');
  3. const { get_conf, get_redis_subscriber } = require('./node_utils');
  4. const conf = get_conf();
  5. const log = console.log; // eslint-disable-line
  6. const subscriber = get_redis_subscriber();
  7. const io = require('socket.io')(conf.socketio_port, {
  8. cors: {
  9. origin: "*", // we are checking for hostnames before registering a socket
  10. }
  11. });
  12. // on socket connection
  13. io.on('connection', function (socket) {
  14. if (get_hostname(socket.request.headers.host) != get_hostname(socket.request.headers.origin)) {
  15. return;
  16. }
  17. if (!socket.request.headers.cookie) {
  18. return;
  19. }
  20. const sid = cookie.parse(socket.request.headers.cookie).sid;
  21. if (!sid) {
  22. return;
  23. }
  24. socket.user = cookie.parse(socket.request.headers.cookie).user_id;
  25. socket.on('task_subscribe', function (task_id) {
  26. var room = get_task_room(socket, task_id);
  27. socket.join(room);
  28. });
  29. let retries = 0;
  30. let join_user_room = () => {
  31. request.get(get_url(socket, '/api/method/frappe.realtime.get_user_info'))
  32. .type('form')
  33. .query({
  34. sid: sid
  35. })
  36. .then(res => {
  37. const room = get_user_room(socket, res.body.message.user);
  38. socket.join(room);
  39. socket.join(get_site_room(socket));
  40. })
  41. .catch(e => {
  42. if (e.code === 'ECONNREFUSED' && retries < 5) {
  43. // retry after 1s
  44. retries += 1;
  45. return setTimeout(join_user_room, 1000);
  46. }
  47. log(`Unable to join user room. ${e}`);
  48. });
  49. };
  50. join_user_room();
  51. socket.on('task_unsubscribe', function (task_id) {
  52. var room = get_task_room(socket, task_id);
  53. socket.leave(room);
  54. });
  55. socket.on('task_unsubscribe', function (task_id) {
  56. var room = 'task:' + task_id;
  57. socket.leave(room);
  58. });
  59. socket.on('progress_subscribe', function (task_id) {
  60. var room = get_task_room(socket, task_id);
  61. socket.join(room);
  62. send_existing_lines(task_id, socket);
  63. });
  64. socket.on('doc_subscribe', function (doctype, docname) {
  65. can_subscribe_doc({
  66. socket,
  67. sid,
  68. doctype,
  69. docname,
  70. callback: () => {
  71. var room = get_doc_room(socket, doctype, docname);
  72. socket.join(room);
  73. }
  74. });
  75. });
  76. socket.on('doc_unsubscribe', function (doctype, docname) {
  77. var room = get_doc_room(socket, doctype, docname);
  78. socket.leave(room);
  79. });
  80. socket.on('doc_open', function (doctype, docname) {
  81. can_subscribe_doc({
  82. socket,
  83. sid,
  84. doctype,
  85. docname,
  86. callback: () => {
  87. var room = get_open_doc_room(socket, doctype, docname);
  88. socket.join(room);
  89. // show who is currently viewing the form
  90. send_users(
  91. {
  92. socket: socket,
  93. doctype: doctype,
  94. docname: docname,
  95. },
  96. 'view'
  97. );
  98. // show who is currently typing on the form
  99. send_users(
  100. {
  101. socket: socket,
  102. doctype: doctype,
  103. docname: docname,
  104. },
  105. 'type'
  106. );
  107. }
  108. });
  109. });
  110. socket.on('doc_close', function (doctype, docname) {
  111. // remove this user from the list of 'who is currently viewing the form'
  112. var room = get_open_doc_room(socket, doctype, docname);
  113. socket.leave(room);
  114. send_users(
  115. {
  116. socket: socket,
  117. doctype: doctype,
  118. docname: docname,
  119. },
  120. 'view'
  121. );
  122. });
  123. socket.on('doc_typing', function (doctype, docname) {
  124. // show users that are currently typing on the form
  125. const room = get_typing_room(socket, doctype, docname);
  126. socket.join(room);
  127. send_users(
  128. {
  129. socket: socket,
  130. doctype: doctype,
  131. docname: docname,
  132. },
  133. 'type'
  134. );
  135. });
  136. socket.on('doc_typing_stopped', function (doctype, docname) {
  137. // remove this user from the list of users currently typing on the form'
  138. const room = get_typing_room(socket, doctype, docname);
  139. socket.leave(room);
  140. send_users(
  141. {
  142. socket: socket,
  143. doctype: doctype,
  144. docname: docname,
  145. },
  146. 'type'
  147. );
  148. });
  149. socket.on('open_in_editor', (data) => {
  150. let s = get_redis_subscriber('redis_socketio');
  151. s.publish('open_in_editor', JSON.stringify(data));
  152. });
  153. });
  154. subscriber.on("message", function (_channel, message) {
  155. message = JSON.parse(message);
  156. if (message.room) {
  157. io.to(message.room).emit(message.event, message.message);
  158. } else {
  159. io.emit(message.event, message.message);
  160. }
  161. });
  162. subscriber.subscribe("events");
  163. function send_existing_lines(task_id, socket) {
  164. var room = get_task_room(socket, task_id);
  165. subscriber.hgetall('task_log:' + task_id, function (_err, lines) {
  166. io.to(room).emit('task_progress', {
  167. "task_id": task_id,
  168. "message": {
  169. "lines": lines
  170. }
  171. });
  172. });
  173. }
  174. function get_doc_room(socket, doctype, docname) {
  175. return get_site_name(socket) + ':doc:' + doctype + '/' + docname;
  176. }
  177. function get_open_doc_room(socket, doctype, docname) {
  178. return get_site_name(socket) + ':open_doc:' + doctype + '/' + docname;
  179. }
  180. function get_typing_room(socket, doctype, docname) {
  181. return get_site_name(socket) + ':typing:' + doctype + '/' + docname;
  182. }
  183. function get_user_room(socket, user) {
  184. return get_site_name(socket) + ':user:' + user;
  185. }
  186. function get_site_room(socket) {
  187. return get_site_name(socket) + ':all';
  188. }
  189. function get_task_room(socket, task_id) {
  190. return get_site_name(socket) + ':task_progress:' + task_id;
  191. }
  192. function get_site_name(socket) {
  193. var hostname_from_host = get_hostname(socket.request.headers.host);
  194. if (socket.request.headers['x-frappe-site-name']) {
  195. return get_hostname(socket.request.headers['x-frappe-site-name']);
  196. } else if (['localhost', '127.0.0.1'].indexOf(hostname_from_host) !== -1 &&
  197. conf.default_site) {
  198. // from currentsite.txt since host is localhost
  199. return conf.default_site;
  200. } else if (socket.request.headers.origin) {
  201. return get_hostname(socket.request.headers.origin);
  202. } else {
  203. return get_hostname(socket.request.headers.host);
  204. }
  205. }
  206. function get_hostname(url) {
  207. if (!url) return undefined;
  208. if (url.indexOf("://") > -1) {
  209. url = url.split('/')[2];
  210. }
  211. return (url.match(/:/g)) ? url.slice(0, url.indexOf(":")) : url;
  212. }
  213. function get_url(socket, path) {
  214. if (!path) {
  215. path = '';
  216. }
  217. return socket.request.headers.origin + path;
  218. }
  219. function can_subscribe_doc(args) {
  220. if (!args) return;
  221. if (!args.doctype || !args.docname) return;
  222. request.get(get_url(args.socket, '/api/method/frappe.realtime.can_subscribe_doc'))
  223. .type('form')
  224. .query({
  225. sid: args.sid,
  226. doctype: args.doctype,
  227. docname: args.docname
  228. })
  229. .end(function (err, res) {
  230. if (!res) {
  231. log("No response for doc_subscribe");
  232. } else if (res.status == 403) {
  233. return;
  234. } else if (err) {
  235. log(err);
  236. } else if (res.status == 200) {
  237. args.callback(err, res);
  238. } else {
  239. log("Something went wrong", err, res);
  240. }
  241. });
  242. }
  243. function send_users(args, action) {
  244. if (!(args && args.doctype && args.docname)) {
  245. return;
  246. }
  247. const open_doc_room = get_open_doc_room(args.socket, args.doctype, args.docname);
  248. const room = action == 'view' ? open_doc_room: get_typing_room(args.socket, args.doctype, args.docname);
  249. const socketio_room = io.sockets.adapter.rooms[room] || {};
  250. // for compatibility with both v1.3.7 and 1.4.4
  251. const clients_dict = ('sockets' in socketio_room) ? socketio_room.sockets : socketio_room;
  252. // socket ids connected to this room
  253. const clients = Object.keys(clients_dict || {});
  254. let users = [];
  255. for (let i in io.sockets.sockets) {
  256. const s = io.sockets.sockets[i];
  257. if (clients.indexOf(s.id) !== -1) {
  258. // this socket is connected to the room
  259. users.push(s.user);
  260. }
  261. }
  262. const emit_event = action == 'view' ? 'doc_viewers' : 'doc_typers';
  263. // notify
  264. io.to(open_doc_room).emit(emit_event, {
  265. doctype: args.doctype,
  266. docname: args.docname,
  267. users: Array.from(new Set(users))
  268. });
  269. }