#!/bin/bash # purge coinos-ui and coinos-server # ie- fresh install/clean working dirs but without the clone - # useful for local dev when you have stashed changes or branches # you don't want to destroy # start script from coinos-server/scripts ie: # $ cd /var/node/coinos-server/scripts # $ ./purge-except-git.sh # you may have to run it with sudo to remove the directories created by docker # and now the script will move up a dir and do its thing: cd ../ # if you have docker-compose process running docker-compose down #purge: rm -rf config rm -rf mysql rm -rf node_modules rm .env rm fx rm docker-compose.override.yml #also on ui: cd ../coinos-ui rm -rf node_modules # (and then follow instructions in root README to rebuild/install) ut type='hidden' name='id' value='f7b844883506a8072213245b9b8a2b988694e4f5'/> coinos server — legacy coinos.io backend (Lightning, on-chain, Liquid)coinos
aboutsummaryrefslogblamecommitdiffstats
path: root/routes/payments.js
blob: 279588d13f21cdd18cc45d56b4d9e3bb02f53b93 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                       
                     
                             
                               
                                      
 
                                                              


                                     
                          
                                            
                                  
                                    


                              
 


                                         
 



                             


                                                                 
 




                                           
 

                                             
 
                                                                
 
                                                   
 




                                                                        
   






                                                                             
                                                       
                          

                                 
 
                                             
                                                 


                                                   

                                                                            
                                                        
 



















                                                                    

                                              










                                                                

                                                




                                                    
               
                                   

    
                                         
                                                                 
                                                             
 
                                                
 
                                                
 
                                           
          



                            
      
              
                                                  


                     

          

     




                                                

   
                                                     





                                            
                            

       
                                           
                                       
 






                                       





































































































                                                                              

                                                           









                                                            
import app from "$app";
import db from "$db";
import config from "$config";
import store from "$lib/store";
import getAccount from "$lib/account";

import { auth, adminAuth, optionalAuth } from "$lib/passport";
import fs from "fs";
import { join } from "path";
import { Op } from "@sequelize/core";
import send from "./send";
import { err, l, warn } from "$lib/logging";
import { fail } from "$lib/utils";
import { emit } from "$lib/sockets";

import bc from "$lib/bitcoin";
import lq from "$lib/liquid";

import btcRoutes from "./bitcoin/index";
import lnRoutes from "./lightning/index";
import lqRoutes from "./liquid/index";

import "./bitcoin/receive";
import "./lightning/receive";
import "./liquid/receive";

app.post("/send", auth, send);
app.post("/sendToTokenHolders", auth, async (req, res, next) => {
  let { asset, amount } = req.body;

  let accounts = await db.Account.findAll({
    where: {
      asset,
      "$user.username$": { [Op.ne]: "gh" }
    },

    include: [{ model: db.User, as: "user" }]
  });

  let totalTokens = accounts.reduce((a, b) => a + b.balance, 0);

  let totalSats = Math.floor(amount / totalTokens);

  if (totalSats < 1) throw new Error("amount is too low to distribute");

  for (let i = 0; i < accounts.length; i++) {
    let account = accounts[i];
    console.log(account.user.username, account.balance);
  }
  accounts.map(({ balance, user: { username } }) => ({ username, balance }));

  res.send({ success: "it worked" });
});

app.get("/except", adminAuth, (req, res) => {
  let s = fs.createWriteStream("exceptions", { flags: "a" });
  store.unaccounted.map(tx => s.write(tx.txid + "\n"));
  l("updated exceptions");
  res.send("updated exceptions");
});

app.post("/lightning/parse", lnRoutes.parse);
app.post("/lightning/channel", lnRoutes.channel);
app.post("/lightning/query", auth, lnRoutes.query);
app.post("/lightning/send", auth, lnRoutes.send);

if (config.lnurl) {
  let { channelRequest } = await import("$routes/lightning/channelRequest");
  app.post("/lightning/channelRequest", channelRequest);
}

app.post("/bitcoin/broadcast", optionalAuth, btcRoutes.broadcast);
app.get("/bitcoin/generate", auth, btcRoutes.generate);
app.post("/bitcoin/sweep", auth, btcRoutes.sweep);
app.post("/bitcoin/fee", auth, btcRoutes.fee);
app.post("/bitcoin/send", auth, btcRoutes.send);

setTimeout(async () => {
  try {
    const address = await bc.getNewAddress();
    const { hdkeypath } = await bc.getAddressInfo(address);
    const parts = hdkeypath.split("/");
    store.bcAddressIndex = parts[parts.length - 1].replace("'", "");
  } catch (e) {
    console.error(e);
  }
}, 50);

app.post("/liquid/broadcast", optionalAuth, lqRoutes.broadcast);
app.get("/liquid/generate", auth, lqRoutes.generate);
app.post("/liquid/fee", auth, lqRoutes.fee);
app.post("/liquid/send", auth, lqRoutes.send);

setTimeout(async () => {
  try {
    const address = await lq.getNewAddress();
    const { hdkeypath } = await lq.getAddressInfo(address);
    const parts = hdkeypath.split("/");
    store.lqAddressIndex = parts[parts.length - 1].slice(0, -1);
  } catch (e) {
    warn("Problem getting liquid address index", e.message);
  }
}, 50);

app.get("/payments", auth, async (req, res) => {
  let { start, end, limit, offset, v2 } = req.query;

  if (limit) limit = parseInt(limit);
  if (offset) offset = parseInt(offset);

  let where = {
    account_id: req.user.account_id
  };

  if (start || end) where.createdAt = {};
  if (start) where.createdAt[Op.gte] = new Date(parseInt(start));
  if (end) where.createdAt[Op.lte] = new Date(parseInt(end));

  if (!req.user.account_id) return res.send([]);

  let total = await db.Payment.count({ where });

  let payments = await db.Payment.findAll({
    where,
    order: [["id", "DESC"]],
    include: {
      model: db.Account,
      as: "account"
    },
    include: {
      attributes: ["username", "profile", "uuid"],
      model: db.User,
      as: "with"
    },
    limit,
    offset
  });

  if (v2) {
    res.send({ transactions: payments, total });
  } else {
    res.send(payments);
  }
});

app.get("/voucher/:redeemcode", async (req, res) => {
  try {
    const { redeemcode } = req.params;
    let payment = await db.Payment.findOne({
      where: {
        redeemcode
      },
      include: { all: true }
    });

    payment = payment.get({ plain: true });
    payment.redeemer = payment["with"];

    if (!payment) fail("invalid code");

    res.send(payment);
  } catch (e) {
    res.code(500).send(e.message);
  }
});

let redeeming = {};
app.post("/redeem", optionalAuth, async function(req, res) {
  const { redeemcode } = req.body;
  try {
    await db.transaction(async transaction => {
      if (redeeming[redeemcode]) fail("redemption in progress");
      redeeming[redeemcode] = true;
      if (!redeemcode) fail("no code provided");

      let { user } = req;

      const source = await db.Payment.findOne({
        where: {
          redeemcode: req.body.redeemcode
        },
        include: {
          model: db.Account,
          as: "account"
        },
        lock: transaction.LOCK.UPDATE,
        transaction
      });

      l("redeeming", redeemcode);

      if (!source) fail("Invalid code");
      if (source.redeemed) fail("Voucher has already been redeemed");
      let { amount } = source;
      amount = -amount;

      if (!user) {
        const ip =
          req.headers["x-forwarded-for"] || req.connection.remoteAddress;

        user = await register(
          {
            username: redeemcode.substr(0, 8),
            password: ""
          },
          ip,
          false
        );

        let payload = { username: user.username };
        let token = jwt.sign(payload, config.jwt);
        res.cookie("token", token, {
          expires: new Date(Date.now() + 432000000)
        });

        delete redeeming[redeemcode];
        return res.send({ user });
      }

      let account = await getAccount(source.account.asset, user, transaction);
      let { hash, memo, confirmed, fee, network } = source;

      source.redeemed = true;
      (source.with_id = user.id), await source.save({ transaction });

      let payment = await db.Payment.create(
        {
          amount,
          account_id: account.id,
          user_id: user.id,
          hash: "Voucher " + redeemcode,
          memo,
          rate: store.rates[user.currency],
          currency: user.currency,
          confirmed,
          network,
          received: true,
          fee,
          with_id: source.user_id
        },
        { transaction }
      );

      await account.increment({ balance: amount }, { transaction });
      await account.reload({ transaction });

      payment = payment.get({ plain: true });
      payment.account = account.get({ plain: true });
      emit(user.username, "payment", payment);
      emit(user.username, "account", account);

      res.send({ payment });
    });
  } catch (e) {
    delete redeeming[redeemcode];
    console.log(e);
    err("problem redeeming", e.message);
    return res.code(500).send("There was a problem redeeming the voucher");
  }
});

app.post("/checkRedeemCode", auth, async function(req, res) {
  const { redeemcode } = req.body;

  const payment = await db.Payment.findOne({ where: { redeemcode } });
  res.send(payment);
});

app.get("/payments/:hash", auth, async function(req, res) {
  try {
    let payment = await db.Payment.findOne({
      where: { user_id: req.user.id, hash: req.params.hash }
    });

    return payment.get({ plain: true });
  } catch (e) {
    console.log(e);
  }
});