Advertisement
Guest User

Untitled

a guest
Oct 19th, 2017
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.41 KB | None | 0 0
  1. 'use strict';
  2.  
  3. const express = require('express'),
  4. bodyParser = require('body-parser'),
  5. // cors = require('cors'),
  6. { Client } = require('pg');
  7.  
  8. // Setup express
  9. const app = express();
  10. // app.use(cors());
  11. app.use(bodyParser.urlencoded({ extended: true }));
  12. app.use(bodyParser.json());
  13. app.set('x-powered-by', false);
  14. app.set('port', process.env.PORT || 5000);
  15.  
  16. app.use(express.static('./public')); // for test purpose
  17.  
  18. // Connect to the database
  19. const client = new Client({
  20. host: process.env.PGHOST || 'localhost',
  21. port: process.env.PGPORT || '5432',
  22. database: process.env.PGDATABASE || 'VirtualRadio',
  23. user: process.env.PGUSER || 'postgres',
  24. password: process.env.PGPASSWORD || 'vradio',
  25. });
  26.  
  27. client.connect();
  28.  
  29. /*
  30.  
  31. Some very interesting stuff:
  32. http://mherman.org/blog/2015/02/12/postgresql-and-nodejs/#.WbBXENhpxpg
  33.  
  34. postgresql port: 5432
  35. pwd: aron
  36.  
  37. /v1/buckets
  38.  
  39. /v1/bucket/objects/ --> list of ids
  40. /v1/bucket/object/:id/ --> download object
  41. stream object as linear audio ?
  42. /v1/bucket/object/:id/location
  43. /v1/bucket/object/:id/metadata
  44.  
  45. /v1/sound/:id/metadata
  46. /v1/sound/:id/marks
  47.  
  48.  
  49. https://musicbrainz.org/doc/Style/Recording
  50. https://www.discogs.com/developers/
  51.  
  52. */
  53.  
  54. // Object store API
  55. // -----------------------------------------------------------------------------
  56.  
  57. /// TODO validation perhaps using https://github.com/ctavan/express-validator
  58. /// https://medium.com/@jeffandersen/building-a-node-js-rest-api-with-express-part-two-9152661bf47
  59.  
  60. // Append an objet
  61. app.post('/v1/object', function(req, res) {
  62. console.log('PARAMS:');
  63. console.log(req.params);
  64. console.log('BODY:');
  65. console.log(req.body);
  66. console.log('QUERY:');
  67. console.log(req.query);
  68. console.log('-------');
  69.  
  70. if (
  71. typeof req.body.uuid === 'string' &&
  72. typeof req.body.location === 'string'
  73. ) {
  74. // 'metadata' parameter optional
  75. // var metadata = req.body.metadata || 'NULL';
  76.  
  77. client.query(
  78. 'INSERT INTO objects (uuid, location, created, modified, ' +
  79. 'metadata, hash) VALUES($1, $2, $3, $4, $5, $6);',
  80. [
  81. req.body.uuid,
  82. req.body.location,
  83. req.body.created || new Date().toISOString(),
  84. req.body.modified || new Date().toISOString(),
  85. req.body.metadata,
  86. req.body.hash,
  87. ],
  88. (err, result) => {
  89. if (err) {
  90. res.status(500);
  91. res.send(err);
  92.  
  93. console.log(JSON.stringify(err));
  94.  
  95. /// error code, key already existing: 23505 unique_violation
  96. /*
  97.  
  98. {
  99. "name": "error",
  100. "length": 249,
  101. "severity": "ERREUR",
  102. "code": "23505",
  103. "detail": "La clé « (uuid)=(d4763006-20af-4bab-bd7a-0201564a1864) » existe déjà.",
  104. "schema": "public",
  105. "table": "objects",
  106. "constraint": "objects_pkey",
  107. "file": "nbtinsert.c",
  108. "line": "433",
  109. "routine": "_bt_check_unique"
  110. }
  111.  
  112.  
  113. */
  114. } else {
  115. res.status(200);
  116. res.send(result);
  117.  
  118. /// result.rowCount
  119. }
  120. },
  121. );
  122. } else {
  123. res.status(400);
  124. res.send({
  125. result: 'error',
  126. error: {
  127. code: 400,
  128. message: 'invalid or missing parameter(s)',
  129. },
  130. });
  131. }
  132. });
  133.  
  134. // Remove an object
  135. app.delete('/v1/object/:uuid', function(req, res) {
  136. if (typeof req.params.uuid === 'string') {
  137. client.query(
  138. 'DELETE FROM objects WHERE uuid = $1;',
  139. [req.params.uuid],
  140. (err, result) => {
  141. if (err) {
  142. res.status(500);
  143. res.send(err);
  144. } else {
  145. res.status(200);
  146. res.send(result);
  147.  
  148. /// result.rowCount
  149. }
  150. },
  151. );
  152. } else {
  153. res.status(400);
  154. }
  155. });
  156.  
  157. app.get('/v1/object', function(req, res) {
  158. console.log('PARAMS:');
  159. console.log(req.params);
  160. console.log('BODY:');
  161. console.log(req.body);
  162. console.log('QUERY:');
  163. console.log(req.query);
  164. console.log('-------');
  165. res.setHeader('Access-Control-Allow-Origin', '*');
  166.  
  167. if (req.query) {
  168. let fields = [],
  169. values = [];
  170.  
  171. if (typeof req.query.uuid === 'string') {
  172. fields.push('uuid');
  173. values.push(req.query.uuid);
  174. }
  175.  
  176. if (typeof req.query.location === 'string') {
  177. fields.push('location');
  178. values.push(req.query.location);
  179. }
  180.  
  181. if (fields.length == 0 || values.length == 0) {
  182. res.status(400);
  183. return;
  184. }
  185.  
  186. let sql = 'SELECT * FROM objects WHERE ',
  187. param = 1;
  188.  
  189. for (let n = 0; n < fields.length; ++n) {
  190. if (n > 0) sql += ' AND ';
  191.  
  192. sql += '(' + fields[n] + '=$' + param++ + ')';
  193. }
  194.  
  195. sql += ';';
  196.  
  197. client.query(sql, values, (err, queryRes) => {
  198. if (err) {
  199. res.status(500).send(err);
  200. } else {
  201. res.status(200).send(queryRes.rows[0] || {}); /// return a more complete object in case of not found
  202. }
  203. });
  204. } else {
  205. res.status(400);
  206. }
  207. });
  208.  
  209. // Get an object by UUID
  210. app.get('/v1/object/:uuid', function(req, res) {
  211. res.setHeader('Access-Control-Allow-Origin', '*');
  212.  
  213. if (typeof req.params.uuid === 'string') {
  214. client.query(
  215. 'SELECT * FROM objects WHERE uuid = $1;',
  216. [req.params.uuid],
  217. (err, queryRes) => {
  218. if (err) {
  219. res.status(500).send(err);
  220. } else {
  221. res.status(200).send(queryRes.rows[0] || {}); /// return a more complete object in case of not found
  222. }
  223. },
  224. );
  225. } else {
  226. res.status(400);
  227. }
  228. });
  229.  
  230. // Modify an objet
  231. app.put('/v1/object/:uuid', function(req, res) {
  232. //// how to handle this case, which parameters are optional ?
  233. //// if parameter is omitted -> no modification so we need to pass an empty string to delete the content of a parameter ?
  234.  
  235. if (
  236. typeof req.params.uuid === 'string' &&
  237. typeof req.body.location === 'string'
  238. ) {
  239. // 'metadata' parameter optional
  240. // var metadata = req.body.metadata || 'NULL';
  241.  
  242. client.query(
  243. 'UPDATE objects SET location=$1, metadata=$2, modified=$3, hash=$4 WHERE uuid=$5;',
  244. [
  245. req.body.location,
  246. req.body.metadata,
  247. req.body.modified || new Date().toISOString(),
  248. req.body.hash,
  249. req.params.uuid,
  250. ],
  251. (err, result) => {
  252. if (err) {
  253. console.log(err);
  254. res.status(500);
  255. res.send(err);
  256.  
  257. /// error code, key already existing: 23505 unique_violation
  258. /*
  259.  
  260. {
  261. "name": "error",
  262. "length": 249,
  263. "severity": "ERREUR",
  264. "code": "23505",
  265. "detail": "La clé « (uuid)=(d4763006-20af-4bab-bd7a-0201564a1864) » existe déjà.",
  266. "schema": "public",
  267. "table": "objects",
  268. "constraint": "objects_pkey",
  269. "file": "nbtinsert.c",
  270. "line": "433",
  271. "routine": "_bt_check_unique"
  272. }
  273.  
  274.  
  275. */
  276. } else {
  277. res.status(200);
  278. res.send(result);
  279.  
  280. /// result.rowCount
  281. }
  282. },
  283. );
  284. } else {
  285. res.status(400);
  286. res.send({
  287. result: 'error',
  288. error: {
  289. code: 400,
  290. message: 'invalid or missing parameter(s)',
  291. },
  292. });
  293. }
  294. });
  295.  
  296. // Sounds database API
  297. // -----------------------------------------------------------------------------
  298. app.post('/v1/sound', function(req, res) {
  299. console.log('PARAMS:');
  300. console.log(req.params);
  301. console.log('BODY:');
  302. console.log(req.body);
  303. console.log('QUERY:');
  304. console.log(req.query);
  305. console.log('-------');
  306.  
  307. if (
  308. typeof req.body.uuid === 'string' &&
  309. typeof req.body.obj_uuid === 'string' &&
  310. typeof req.body.title === 'string'
  311. ) {
  312. let duration = req.body.duration || 0,
  313. cue_in = req.body.cue_in || 0,
  314. cue_out = req.body.cue_out || duration,
  315. eff_duration = req.body.eff_duration || duration;
  316.  
  317. // 'metadata' parameter optional
  318. // var metadata = req.body.metadata || 'NULL';
  319.  
  320. client.query(
  321. 'INSERT INTO sounds (uuid, obj_uuid, duration, cue_in, ' +
  322. 'cue_out, eff_duration, created, modified, title, ' +
  323. 'artist) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);',
  324. [
  325. req.body.uuid,
  326. req.body.obj_uuid,
  327. duration,
  328. cue_in,
  329. cue_out,
  330. eff_duration,
  331. req.body.created || new Date().toISOString(),
  332. req.body.modified || new Date().toISOString(),
  333. req.body.title,
  334. req.body.artist,
  335. ],
  336. (err, result) => {
  337. if (err) {
  338. res.status(500);
  339. res.send(err);
  340.  
  341. console.log(JSON.stringify(err));
  342.  
  343. /// error code, key already existing: 23505 unique_violation
  344. /*
  345.  
  346. {
  347. "name": "error",
  348. "length": 249,
  349. "severity": "ERREUR",
  350. "code": "23505",
  351. "detail": "La clé « (uuid)=(d4763006-20af-4bab-bd7a-0201564a1864) » existe déjà.",
  352. "schema": "public",
  353. "table": "objects",
  354. "constraint": "objects_pkey",
  355. "file": "nbtinsert.c",
  356. "line": "433",
  357. "routine": "_bt_check_unique"
  358. }
  359.  
  360.  
  361. */
  362. } else {
  363. res.status(200);
  364. res.send(result);
  365.  
  366. /// result.rowCount
  367. }
  368. },
  369. );
  370. } else {
  371. res.status(400);
  372. res.send({
  373. result: 'error',
  374. error: {
  375. code: 400,
  376. message: 'invalid or missing parameter(s)',
  377. },
  378. });
  379. }
  380. });
  381.  
  382. app.put('/v1/sound/:uuid', function(req, res) {
  383. console.log('PARAMS:');
  384. console.log(req.params);
  385. console.log('BODY:');
  386. console.log(req.body);
  387. console.log('QUERY:');
  388. console.log(req.query);
  389. console.log('-------');
  390.  
  391. if (
  392. typeof req.body.uuid === 'string' &&
  393. typeof req.body.obj_uuid === 'string' &&
  394. typeof req.body.title === 'string'
  395. ) {
  396. let duration = req.body.duration || 0,
  397. cue_in = req.body.cue_in || 0,
  398. cue_out = req.body.cue_out || duration,
  399. eff_duration = req.body.eff_duration || duration;
  400.  
  401. // 'metadata' parameter optional
  402. // var metadata = req.body.metadata || 'NULL';
  403.  
  404. client.query(
  405. 'UPDATE sounds SET obj_uuid=$1, duration=$2, cue_in=$3, ' +
  406. 'cue_out=$4, eff_duration=$5, created=$6, modified=$7, ' +
  407. 'title=$8, artist=$9 WHERE uuid=$10;',
  408. [
  409. req.body.obj_uuid,
  410. duration,
  411. cue_in,
  412. cue_out,
  413. eff_duration,
  414. req.body.created || new Date().toISOString(),
  415. req.body.modified || new Date().toISOString(),
  416. req.body.title,
  417. req.body.artist,
  418. req.body.uuid,
  419. ],
  420. (err, result) => {
  421. if (err) {
  422. res.status(500);
  423. res.send(err);
  424.  
  425. console.log(JSON.stringify(err));
  426.  
  427. /// error code, key already existing: 23505 unique_violation
  428. /*
  429.  
  430. {
  431. "name": "error",
  432. "length": 249,
  433. "severity": "ERREUR",
  434. "code": "23505",
  435. "detail": "La clé « (uuid)=(d4763006-20af-4bab-bd7a-0201564a1864) » existe déjà.",
  436. "schema": "public",
  437. "table": "objects",
  438. "constraint": "objects_pkey",
  439. "file": "nbtinsert.c",
  440. "line": "433",
  441. "routine": "_bt_check_unique"
  442. }
  443.  
  444.  
  445. */
  446. } else {
  447. res.status(200);
  448. res.send(result);
  449.  
  450. /// result.rowCount
  451. }
  452. },
  453. );
  454. } else {
  455. res.status(400);
  456. res.send({
  457. result: 'error',
  458. error: {
  459. code: 400,
  460. message: 'invalid or missing parameter(s)',
  461. },
  462. });
  463. }
  464. });
  465.  
  466. app.get('/v1/sound/search', function(req, res) {
  467. res.setHeader('Access-Control-Allow-Origin', '*');
  468.  
  469. if (req.query.query) {
  470. // Prepare the query
  471. let sql =
  472. 'SELECT sounds.*, objects.location, count(*) OVER() AS total_rows ' +
  473. 'FROM sounds INNER JOIN objects ON sounds.obj_uuid = ' +
  474. 'objects.uuid WHERE ',
  475. paramIdx = 1,
  476. params = [],
  477. where = [],
  478. queries = req.query.query.split(' ');
  479.  
  480. queries.forEach(q => {
  481. where.push(
  482. '(LOWER(title) LIKE $' +
  483. paramIdx++ +
  484. ' OR LOWER(artist) ' +
  485. 'LIKE $' +
  486. paramIdx++ +
  487. ')',
  488. );
  489.  
  490. let pattern = `%${q.trim().toLowerCase()}%`;
  491. params.push(pattern);
  492. params.push(pattern);
  493. });
  494.  
  495. for (let n = 0; n < where.length; ++n) {
  496. if (n > 0) sql += ' AND ';
  497.  
  498. sql += where[n];
  499. }
  500.  
  501. sql +=
  502. ' ORDER BY artist ASC, title ASC LIMIT $' +
  503. paramIdx++ +
  504. ' OFFSET $' +
  505. paramIdx++ +
  506. ';';
  507.  
  508. params.push(req.query.limit || 100);
  509. params.push(req.query.offset || 0);
  510.  
  511. // Run the query
  512. client.query(sql, params, (err, queryRes) => {
  513. if (err) {
  514. res.status(500).send(err);
  515. } else {
  516. let answer = {};
  517. answer.result = 'success';
  518. answer.totalRowCount = 0;
  519. answer.rowCount = queryRes.rowCount;
  520.  
  521. if (answer.rowCount) {
  522. answer.totalRowCount = queryRes.rows[0].total_rows;
  523.  
  524. // Remove total_rows column from all rows
  525. queryRes.rows.forEach(row => {
  526. delete row.total_rows;
  527. });
  528.  
  529. answer.rows = queryRes.rows;
  530. }
  531.  
  532. res.status(200).send(answer);
  533. }
  534. });
  535. } else {
  536. res.status(400).send({
  537. result: 'error',
  538. error: {
  539. code: 400,
  540. message: 'invalid or missing parameter(s)',
  541. },
  542. });
  543. }
  544. });
  545.  
  546. app.get('/v1/sound/:uuid', function(req, res) {
  547. res.setHeader('Access-Control-Allow-Origin', '*');
  548.  
  549. if (typeof req.params.uuid === 'string') {
  550. client.query(
  551. 'SELECT * FROM sounds WHERE uuid=$1;',
  552. [req.params.uuid],
  553. (err, queryRes) => {
  554. if (err) {
  555. res.status(500).send(err);
  556. } else {
  557. res.status(200).send(queryRes.rows[0] || {});
  558. }
  559. },
  560. );
  561. } else {
  562. res.status(400);
  563. res.send({
  564. result: 'error',
  565. error: {
  566. code: 400,
  567. message: 'invalid or missing parameter(s)',
  568. },
  569. });
  570. }
  571. });
  572.  
  573. // Error management
  574. // -----------------------------------------------------------------------------
  575.  
  576. // Custom 404 page
  577. app.use(function(req, res) {
  578. res.type('text/plain');
  579. res.status(404);
  580. res.send('404 - Not Found');
  581. });
  582.  
  583. // Custom 500 page
  584. app.use(function(err, req, res, next) {
  585. console.log(err.stack);
  586. res.type('text/plain');
  587. res.status(500);
  588. res.send('500 - Server Error');
  589. });
  590.  
  591. app.listen(app.get('port'), function() {
  592. console.log('server started on port ' + app.get('port'));
  593. console.log('Press Ctrl-C to terminate');
  594. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement