app.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import 'dotenv/config'
  2. import grpc from "grpc"
  3. import express, { Request } from "express"
  4. import bodyParser from "body-parser"
  5. import basicAuth from "express-basic-auth"
  6. import {
  7. CommandService_v1Client as CommandService,
  8. QueryService_v1Client as QueryService
  9. } from 'iroha-helpers/lib/proto/endpoint_grpc_pb'
  10. import { queries } from 'iroha-helpers'
  11. import util from 'util'
  12. import { exec } from 'child_process'
  13. import cors from 'cors'
  14. import * as sql from 'sqlite3'
  15. const app = express();
  16. app.use(bodyParser.json());
  17. //TODO is cors package necesary? basic middleware could suffice
  18. app.use(cors()); //TODO: set only safe origins
  19. app.use(basicAuth({
  20. users: { admin: 'superPasswd' }
  21. }));
  22. const asyncExec = util.promisify(exec);
  23. const IROHA_ADMIN_PRIV = "f101537e319568c765b2cc89698325604991dca57b9716b58016b253506cab70";
  24. const IROHA_ADMIN = "admin@test";
  25. const IROHA_ADDRESS = process.env.IROHA_ADDRESS || "localhost:50051";
  26. const queryService = new QueryService(IROHA_ADDRESS, grpc.credentials.createInsecure());
  27. const CHAIN4ALL_RASTER_CLIP_SCRIPT_PATH = process.env.CHAIN4ALL_RASTER_CLIP_SCRIPT_PATH || '/home/kunickyd/Documents/chain4all/chain4all_raster_clip.sh';
  28. const CHAIN4ALL_SERVICE_PORT = process.env.CHAIN4ALL_SERVICE_PORT || 3000;
  29. const PRICE_MODIFIER: number = parseFloat(process.env.PRICE_MODIFIER || "0.5");
  30. const DB_FILE_NAME: string = "data/transfers.db";
  31. app.get("/", (req, res) => {
  32. res.send("Chain4All Blockchain service");
  33. });
  34. app.get("/transactions", async (req, res, next) => {
  35. let db = getDbConnection();
  36. db.all(
  37. "SELECT hash, user " +
  38. "FROM transfers " +
  39. "ORDER BY id DESC " +
  40. "LIMIT 10;",
  41. [],
  42. (err, rows) => {
  43. if(err){
  44. next(err);
  45. return;
  46. }
  47. console.log(rows);
  48. }
  49. );
  50. });
  51. app.post("/transaction", async (req, res, next) => {
  52. let db = getDbConnection();
  53. });
  54. app.post("/price", (req, res) => { //add caching of same requests
  55. if (req.body && req.body.area) {
  56. res.send({ price: getPrice(req.body.area) });
  57. }
  58. else {
  59. res.status(400);
  60. throw Error(JSON.stringify({ error: { name: "Error, request has no body with \"area\" property!" } }));
  61. }
  62. });
  63. app.post("/buy", async (req, res, next) => {
  64. try {
  65. if (!req.body) {
  66. res.status(400);
  67. throw Error(JSON.stringify({ error: { name: "Error, request has no body!" } }));
  68. }
  69. if (!req.body.txHash) {
  70. res.status(400);
  71. throw Error(JSON.stringify({ error: { name: "Error, request body has no \"txHash\" property!" } }));
  72. }
  73. //TODO load from http headers, or something like that??
  74. if (!req.body.user) {
  75. res.status(400);
  76. throw Error(JSON.stringify({ error: { name: "Error, request body has no \"user\" property!" } }));
  77. }
  78. //TODO validate this properly
  79. let txDetail = await getTransactionDetail(req.body.txHash, req.body.user);
  80. let extent: number[] = JSON.parse(txDetail.description).extent as number[];
  81. let dataFileId: string = Date.now().toString();
  82. let dataCommand = CHAIN4ALL_RASTER_CLIP_SCRIPT_PATH + ' ' + extent[0] + ' ' + extent[1] + ' ' + extent[2] + ' ' + extent[3] + ' ' + dataFileId;
  83. console.debug(dataCommand);
  84. const { stdout, stderr } = await asyncExec(dataCommand);
  85. console.debug(stdout);
  86. if (stderr) {
  87. console.warn(stderr);
  88. }
  89. res.send({ dataUrl: "https://gis.lesprojekt.cz/chain4all/raster_" + dataFileId + ".tif" });
  90. }
  91. catch (err) {
  92. next(err);
  93. }
  94. });
  95. function getDbConnection() {
  96. let db = new sql.Database(DB_FILE_NAME);
  97. db.run(
  98. "CREATE TABLE [IF NOT EXISTS] transfers ( " +
  99. "id INTEGER PRIMARY KEY, " +
  100. "hash TEXT NOT NULL, " +
  101. "user TEXT NOT NULL, " +
  102. ") [WITHOUT ROWID]; "
  103. );
  104. return db;
  105. }
  106. async function getTransactionDetail(txHash: string, user: string) {
  107. let quer: any = await queries.getAccountTransactions({
  108. privateKey: IROHA_ADMIN_PRIV,
  109. creatorAccountId: IROHA_ADMIN,
  110. queryService,
  111. timeoutLimit: 5000
  112. }, {
  113. accountId: user,
  114. pageSize: 10,
  115. firstTxHash: txHash,
  116. ordering: {
  117. field: undefined,
  118. direction: undefined
  119. },
  120. firstTxTime: undefined,
  121. lastTxTime: undefined,
  122. firstTxHeight: 1,
  123. lastTxHeight: 3
  124. });
  125. //TODO find better way to look for transferAssets command in transaction
  126. return quer.transactionsList[0].payload.reducedPayload.commandsList[0].transferAsset;
  127. }
  128. function getPrice(area: number): number {
  129. return area * PRICE_MODIFIER;
  130. }
  131. function errorMiddleware(err: any, req: any, res: any, next: any): void { //TODO: add custom Exception class
  132. console.log(err);
  133. res.status(500);
  134. res.send(err);
  135. }
  136. app.use(errorMiddleware);
  137. app.listen(CHAIN4ALL_SERVICE_PORT, () => {
  138. console.log(`Listening at http://localhost:${CHAIN4ALL_SERVICE_PORT}`)
  139. });