From 1ac8f4279e0e53e2d4e18b59ff4b54acee2d2c57 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 15 Dec 2022 13:35:30 -0800 Subject: prettier --- db/index.js | 1 - 1 file changed, 1 deletion(-) (limited to 'db') diff --git a/db/index.js b/db/index.js index 0437ece6..e88c8432 100644 --- a/db/index.js +++ b/db/index.js @@ -152,7 +152,6 @@ Invoice.hasOne(Request, { foreignKey: "invoice_id" }); - db.Order = Order; export default db; -- cgit v1.2.3 From 316039601e5170d6f753cf09eac92a94176b4756 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 21 Dec 2022 21:50:06 -0800 Subject: store nostr keys --- db/models/users.js | 9 +++++++++ lib/nostr.js | 2 +- lib/whitelist.js | 3 +++ routes/events.js | 7 +++++++ routes/users.js | 7 +++++++ 5 files changed, 27 insertions(+), 1 deletion(-) (limited to 'db') diff --git a/db/models/users.js b/db/models/users.js index faf41576..fd38facf 100644 --- a/db/models/users.js +++ b/db/models/users.js @@ -224,6 +224,15 @@ const attributes = { comment: null, field: "twofa" }, + cipher: { + type: DataTypes.STRING(255), + }, + pubkey: { + type: DataTypes.STRING(255), + }, + salt: { + type: DataTypes.STRING(255), + }, pin: { type: DataTypes.STRING(255), allowNull: true, diff --git a/lib/nostr.js b/lib/nostr.js index 677f14e5..c75955bd 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -7,12 +7,12 @@ import redis from "$lib/redis"; const { relays } = await got("https://nostr.watch/relays.json").json(); export const pool = RelayPool(relays); -let coinos; pool.on("open", relay => { if (relay.url.includes("coinos")) coinos = relay; relay.subscribe("live", { limit: 1 }); }); +let coinos; let seen = []; let timeout; pool.on("event", async (relay, sub, ev) => { diff --git a/lib/whitelist.js b/lib/whitelist.js index 93e601c7..5a9c0ba3 100644 --- a/lib/whitelist.js +++ b/lib/whitelist.js @@ -5,6 +5,7 @@ export default [ "address", "admin", "banner", + "cipher", "currencies", "currency", "email", @@ -14,7 +15,9 @@ export default [ "index", "keys", "payments", + "pubkey", "profile", + "salt", "seed", "symbol", "theme", diff --git a/routes/events.js b/routes/events.js index 460d09a5..ab682b46 100644 --- a/routes/events.js +++ b/routes/events.js @@ -28,3 +28,10 @@ app.get("/:pubkey/events", async (req, res) => { res.code(500).send("problem fetching user events"); } }); + +app.post('/nostr/send', async (req, res) => { + let { event } = req.body; + pool.send(["EVENT", event]); + res.send(event); +}); + diff --git a/routes/users.js b/routes/users.js index 8d45e8c3..82a71549 100644 --- a/routes/users.js +++ b/routes/users.js @@ -132,6 +132,8 @@ app.post("/user", auth, async (req, res) => { let { user } = req; let { address, + cipher, + pubkey, confirm, currencies, currency, @@ -140,6 +142,7 @@ app.post("/user", auth, async (req, res) => { password, newpin, pin, + salt, seed, tokens, twofa, @@ -170,6 +173,10 @@ app.post("/user", auth, async (req, res) => { } if (unit) user.unit = unit; + if (cipher) user.cipher = cipher; + if (pubkey) user.pubkey = pubkey; + if (salt) user.salt = salt; + user.currency = currency; user.currencies = currencies; user.pin = newpin; -- cgit v1.2.3 From bd47fe6e17139a3afdbaef92a65cf9b5afba75a1 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 31 Dec 2022 07:21:59 +0000 Subject: default relays --- db/models/users.js | 4 +++- lib/nostr.js | 5 ++++- lib/utils.js | 1 - 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'db') diff --git a/db/models/users.js b/db/models/users.js index fd38facf..cc63ac57 100644 --- a/db/models/users.js +++ b/db/models/users.js @@ -253,7 +253,9 @@ const attributes = { const options = { tableName: "users", comment: "", - indexes: [] + indexes: [], + charset: "utf8mb4", + collate: "utf8mb4_unicode_ci" }; db["User"] = db.define("users_model", attributes, options); diff --git a/lib/nostr.js b/lib/nostr.js index dc400b7f..3894819d 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -4,7 +4,10 @@ import { broadcast } from "$lib/sockets"; import store from "$lib/store"; import redis from "$lib/redis"; -const { relays } = await got("https://nostr.watch/relays.json").json(); +const relays = [ + "wss://relay.damus.io", + "wss://nostr.coinos.io" +]; export const pool = RelayPool(relays); diff --git a/lib/utils.js b/lib/utils.js index 0d2d3efb..748b2a74 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -8,7 +8,6 @@ import { payments as bcPayments, networks as bcNetworks } from "bitcoinjs-lib"; import { payments as lqPayments, networks as lqNetworks } from "liquidjs-lib"; import lnd from "$lib/lnd"; import ln from "$lib/ln"; -import { coinos } from "$lib/nostr"; const { Op } = sequelize; -- cgit v1.2.3 From 8266d3adad8d093212b208ff93e6bee41f3370e3 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 6 Jan 2023 13:59:03 -0800 Subject: locktime field, uuid for token --- db/models/users.js | 3 +++ lib/nostr.js | 61 +++++++++++++++++++++++++++++++++++------------------- lib/passport.js | 14 +++++++++---- lib/whitelist.js | 1 + routes/users.js | 31 +++++++++++---------------- 5 files changed, 66 insertions(+), 44 deletions(-) (limited to 'db') diff --git a/db/models/users.js b/db/models/users.js index cc63ac57..23dab0bf 100644 --- a/db/models/users.js +++ b/db/models/users.js @@ -247,6 +247,9 @@ const attributes = { }, phone: { type: DataTypes.STRING(255) + }, + locktime: { + type: DataTypes.INTEGER(11) } }; diff --git a/lib/nostr.js b/lib/nostr.js index 99265f7d..4730883f 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -5,22 +5,16 @@ import { broadcast } from "$lib/sockets"; import store from "$lib/store"; import redis from "$lib/redis"; import proxy from "@fastify/http-proxy"; +import { wait } from "$lib/utils"; app.register(proxy, { upstream: config.nostr, prefix: "/nostr", rewritePrefix: "", - disableRequestLogging: true, + disableRequestLogging: true }); -const relays = [ - config.relay, - "wss://relay.damus.io", - "wss://nostr-pub.wellorder.net", - "wss://nostr.rocks", -]; - -export const pool = RelayPool(relays); +export const pool = RelayPool(config.relays); pool.on("open", relay => { if (relay.url.includes(config.relay)) coinos = relay; @@ -31,38 +25,63 @@ export let coinos; let now = () => Math.round(Date.now() / 1000); let timeouts = {}; -export let q = async (sub, query, timeout = 8000) => +export let q = async (sub, query, timeout = 20000) => new Promise(async (r, j) => { + let start = Date.now(); + let seen = []; let rejected; - if (!coinos) return j(new Error("relay not initialized")); query.since = await redis.get(`since:${sub}`); if (now() - query.since < 3600) return r(); + let done = { [sub]: [] }; - coinos.on("eose", s => { - return !rejected && s === sub && r(timeouts[sub].clear()); - }); + let check = s => { + let elapsed = Date.now() - start; + if (rejected) return true; + if (done[s].length === pool.relays.length || elapsed > 200) { + r(); + if (timeouts[s]) timeouts[s].clear(); + return true; + } + }; timeouts[sub] = { async clear() { clearTimeout(this.timer); delete timeouts[sub]; - coinos.unsubscribe(sub); + pool.unsubscribe(sub); await redis.set(`since:${sub}`, now()); }, - extend(t = timeout) { + extend(ev) { + if (ev) { + if (seen.includes(ev.id)) return; + seen.push(ev.id); + seen.length > 1000 && seen.shift(); + } + clearTimeout(this.timer); this.timer = setTimeout(() => { j(new Error(`query timed out: ${sub} ${Date.now()}`)); - coinos.unsubscribe(sub); + pool.unsubscribe(sub); rejected = true; - }, t); + }, timeout); }, timer: undefined }; - timeouts[sub].extend(timeout); - coinos.subscribe(sub, query); + timeouts[sub].extend(); + pool.subscribe(sub, query); + + pool.on("eose", (relay, s) => { + if (done[s]) done[s].push(relay.url); + check(s); + }); + + try { + await wait(() => check(sub)); + } catch (e) { + console.log(e); + } }); let seen = []; @@ -75,7 +94,7 @@ let getUser = async pubkey => }; pool.on("event", async (relay, sub, ev) => { - if (timeouts[sub]) timeouts[sub].extend(); + if (timeouts[sub]) timeouts[sub].extend(ev); try { let content; try { diff --git a/lib/passport.js b/lib/passport.js index ac2949a5..1244aab5 100644 --- a/lib/passport.js +++ b/lib/passport.js @@ -35,13 +35,19 @@ fastifyPassport.use( }, async (payload, next) => { try { - let { username } = payload; + let { username, uuid } = payload; + let where = {}; + + if (username) + where.username = { [Op.or]: [username, username.replace(/ /g, "")] }; + + if (uuid) where.uuid = uuid; + let user = await db.User.findOne({ - where: { - username: { [Op.or]: [username, username.replace(/ /g, "")] } - }, + where, include: { model: db.Account, as: "account" } }); + if (user.locked) throw new Error("account is locked"); next(null, user); } catch (e) { diff --git a/lib/whitelist.js b/lib/whitelist.js index e78bc8e5..4e4dc14e 100644 --- a/lib/whitelist.js +++ b/lib/whitelist.js @@ -16,6 +16,7 @@ export default [ "id", "index", "keys", + "locktime", "payments", "pubkey", "profile", diff --git a/routes/users.js b/routes/users.js index c935a137..764d1f03 100644 --- a/routes/users.js +++ b/routes/users.js @@ -4,13 +4,10 @@ import config from "$config"; import store from "$lib/store"; import { auth, optionalAuth } from "$lib/passport"; import { getUser, uniq, wait } from "$lib/utils"; -import axios from "axios"; import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; import { authenticator } from "otplib"; import Sequelize from "@sequelize/core"; -import bitcoin from "bitcoinjs-lib"; -import liquid from "liquidjs-lib"; import { fromBase58, fromPrivateKey } from "bip32"; import { Mutex } from "async-mutex"; import bip32 from "bip32"; @@ -191,7 +188,7 @@ app.post("/user", auth, async (req, res) => { let { confirm, password, pin, newpin, username } = req.body; if (user.pin && !(pin === user.pin)) throw new Error("Pin required"); - if (typeof newpin !== 'undefined') user.pin = newpin; + if (typeof newpin !== "undefined") user.pin = newpin; if (!user.pin) user.pin = null; let exists; @@ -210,26 +207,22 @@ app.post("/user", auth, async (req, res) => { if (user.username !== username) l("changing username", user.username, username); user.username = username; - - token = jwt.sign({ username }, config.jwt); - res.cookie("token", token, { - expires: new Date(Date.now() + 432000000) - }); } let attributes = [ - "unit", + "address", "cipher", + "currencies", + "currency", + "email", + "fiat", + "locktime", "pubkey", "salt", - "currency", - "currencies", + "seed", "tokens", "twofa", - "seed", - "fiat", - "email", - "address" + "unit" ]; for (let a of attributes) { @@ -356,8 +349,8 @@ let login = async (req, res) => { try { const url = `${callback}&sig=${sig}&key=${key}`; - const response = await axios.get(url); - res.send(response.data); + const response = await got(url).json(); + res.send(response); } catch (e) { err("problem calling lnurl login", e.message); res.code(500).send(e.message); @@ -393,7 +386,7 @@ let login = async (req, res) => { req.headers["x-forwarded-for"] || req.connection.remoteAddress ); - let payload = { username: user.username }; + let payload = { uuid: user.uuid }; let token = jwt.sign(payload, config.jwt); res.cookie("token", token, { expires: new Date(Date.now() + 432000000) }); user.accounts = await user.getAccounts(); -- cgit v1.2.3 From 6f7ad86583ee4e9e359829b55cb041d014902e07 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 6 Jan 2023 14:11:52 -0800 Subject: prettier --- db/models/users.js | 10 +++++----- routes/users.js | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'db') diff --git a/db/models/users.js b/db/models/users.js index 23dab0bf..72680fd8 100644 --- a/db/models/users.js +++ b/db/models/users.js @@ -1,5 +1,5 @@ import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; +import { DataTypes } from "@sequelize/core"; const attributes = { id: { @@ -140,7 +140,7 @@ const attributes = { unit: { type: DataTypes.STRING(255), allowNull: true, - defaultValue: 'SAT', + defaultValue: "SAT", primaryKey: false, autoIncrement: false, comment: null, @@ -225,13 +225,13 @@ const attributes = { field: "twofa" }, cipher: { - type: DataTypes.STRING(255), + type: DataTypes.STRING(255) }, pubkey: { - type: DataTypes.STRING(255), + type: DataTypes.STRING(255) }, salt: { - type: DataTypes.STRING(255), + type: DataTypes.STRING(255) }, pin: { type: DataTypes.STRING(255), diff --git a/routes/users.js b/routes/users.js index 20b3bfee..d2cb1540 100644 --- a/routes/users.js +++ b/routes/users.js @@ -363,8 +363,7 @@ let login = async (req, res) => { if ( !user || - (user.password && - !(await bcrypt.compare(password, user.password))) + (user.password && !(await bcrypt.compare(password, user.password))) ) { warn("invalid username or password attempt", username); return res.code(401).send({}); -- cgit v1.2.3 From e3266ddecd825b8716df7dc418246441d4dcb0f7 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 6 Jan 2023 17:31:22 -0800 Subject: background nostr fetching --- db/models/users.js | 6 ++++++ lib/nostr.js | 21 +++++++++++-------- lib/sockets.js | 7 +++++-- lib/utils.js | 7 ++++++- lib/whitelist.js | 4 +++- routes/events.js | 5 +++-- routes/users.js | 59 ++++++++++++++++++++++++++++++++++++++++-------------- 7 files changed, 80 insertions(+), 29 deletions(-) (limited to 'db') diff --git a/db/models/users.js b/db/models/users.js index 72680fd8..fce79537 100644 --- a/db/models/users.js +++ b/db/models/users.js @@ -250,6 +250,12 @@ const attributes = { }, locktime: { type: DataTypes.INTEGER(11) + }, + prompt: { + type: DataTypes.BOOLEAN + }, + display: { + type: DataTypes.STRING(255) } }; diff --git a/lib/nostr.js b/lib/nostr.js index 4730883f..4ee5cb13 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -25,20 +25,24 @@ export let coinos; let now = () => Math.round(Date.now() / 1000); let timeouts = {}; -export let q = async (sub, query, timeout = 20000) => +export let q = async (sub, query, timeout = 20000, since = 3600, eager = 200) => new Promise(async (r, j) => { let start = Date.now(); let seen = []; let rejected; query.since = await redis.get(`since:${sub}`); - if (now() - query.since < 3600) return r(); + if (now() - query.since < since) return r(); let done = { [sub]: [] }; let check = s => { let elapsed = Date.now() - start; if (rejected) return true; - if (done[s].length === pool.relays.length || elapsed > 200) { + if ( + done[s] && + (done[s].length === pool.relays.length || + (done[s].length && elapsed > eager)) + ) { r(); if (timeouts[s]) timeouts[s].clear(); return true; @@ -78,7 +82,7 @@ export let q = async (sub, query, timeout = 20000) => }); try { - await wait(() => check(sub)); + await wait(() => check(sub), 100, 200); } catch (e) { console.log(e); } @@ -137,6 +141,7 @@ pool.on("event", async (relay, sub, ev) => { await redis.set(`user:${pubkey}`, JSON.stringify(user)); await redis.set(`user:${user.username}`, JSON.stringify(user)); + coinos.send(ev); } else if (sub.includes("follows")) { let pubkey = sub.split(":")[0]; await redis.set(`${pubkey}:follows`, JSON.stringify(ev.tags)); @@ -144,14 +149,14 @@ pool.on("event", async (relay, sub, ev) => { let [_, followPubkey] = f; await redis.sAdd(`${followPubkey}:followers`, pubkey); } + coinos.send(ev); } else if (sub.includes("followers")) { let followed = sub.split(":")[0]; let { pubkey } = ev; await redis.sAdd(`${followed}:followers`, pubkey); - } else { - if (!relay.url.includes("coinos")) return; - - let pubkey = sub; + coinos.send(ev); + } else if (sub.includes("notes")) { + let pubkey = sub.split(":")[0]; ev.user = await getUser(pubkey); if (!ev.user.updated) { diff --git a/lib/sockets.js b/lib/sockets.js index 3bd0c2c2..82cf3f51 100644 --- a/lib/sockets.js +++ b/lib/sockets.js @@ -85,6 +85,7 @@ app.register(async function(app) { break; case "login": + let user; let token = data; if (lastSeen[token] > subtractSeconds(5)) return; lastSeen[token] = new Date(); @@ -92,7 +93,9 @@ app.register(async function(app) { try { if (!token || token === "null" || !jwt.decode(token)) return ws.close(code, `bad token ${token}`); - const { username, uuid } = jwt.decode(token); + let { username, uuid } = jwt.decode(token); + if (uuid) user = await getUser(uuid); + username = user.username; if (!(username || uuid)) throw new Error("Invalid JWT token"); if (!store.sockets[username]) store.sockets[username] = {}; @@ -110,7 +113,7 @@ app.register(async function(app) { store.sockets[username][id] = ws; names[id] = username; - let user = await getUser(username); + if (!user) user = await getUser(username); let { haspin } = user; diff --git a/lib/utils.js b/lib/utils.js index 748b2a74..95ee5fb1 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -11,6 +11,8 @@ import ln from "$lib/ln"; const { Op } = sequelize; +export const nada = () => {}; + export const sleep = n => new Promise(r => setTimeout(r, n)); export const wait = async (f, s = 300, n = 50) => { let i = 0; @@ -33,7 +35,10 @@ export const getUser = async (username, transaction) => { } ], where: { - username: { [Op.or]: [username, username.replace(/ /g, "")] } + [Op.or]: { + username: { [Op.or]: [username, username.replace(/ /g, "")] }, + uuid: { [Op.or]: [username, username.replace(/ /g, "")] } + } } }; diff --git a/lib/whitelist.js b/lib/whitelist.js index 4e4dc14e..96db6cdc 100644 --- a/lib/whitelist.js +++ b/lib/whitelist.js @@ -8,6 +8,7 @@ export default [ "cipher", "currencies", "currency", + "display", "email", "fiat", "follows", @@ -18,8 +19,9 @@ export default [ "keys", "locktime", "payments", - "pubkey", "profile", + "prompt", + "pubkey", "salt", "seed", "symbol", diff --git a/routes/events.js b/routes/events.js index 1734af24..bbdd8891 100644 --- a/routes/events.js +++ b/routes/events.js @@ -26,14 +26,15 @@ app.get("/nostr/:pubkey", async (req, res) => { followers: [] }; - await q(pubkey, { + q(`${pubkey}:notes`, { kinds: [1], authors: [pubkey] - }); + }).catch(console.log); await redis.set(`user:${pubkey}`, JSON.stringify(user)); let ids = await redis.sMembers(pubkey); + console.log("IDS", ids); let events = ids.length ? (await redis.mGet(ids.map(k => "ev:" + k))).map(JSON.parse) diff --git a/routes/users.js b/routes/users.js index d2cb1540..d209c3d9 100644 --- a/routes/users.js +++ b/routes/users.js @@ -3,7 +3,7 @@ import redis from "$lib/redis"; import config from "$config"; import store from "$lib/store"; import { auth, optionalAuth } from "$lib/passport"; -import { getUser, uniq, wait } from "$lib/utils"; +import { getUser, nada, uniq, wait } from "$lib/utils"; import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; import { authenticator } from "otplib"; @@ -74,7 +74,9 @@ app.get("/users/:username", async (req, res) => { "profile", "address", "currency", - "pubkey" + "pubkey", + "display", + "prompt" ], where: { [Sequelize.Op.or]: [ @@ -214,9 +216,11 @@ app.post("/user", auth, async (req, res) => { "cipher", "currencies", "currency", + "display", "email", "fiat", "locktime", + "prompt", "pubkey", "salt", "seed", @@ -654,11 +658,17 @@ app.get("/:pubkey/follows", async (req, res) => { let { tagsonly } = req.query; let sub = `${pubkey}:follows`; - await q(sub, { - limit: 1, - kinds: [3], - authors: [pubkey] - }); + q( + sub, + { + limit: 1, + kinds: [3], + authors: [pubkey] + }, + 60000, + 3600, + 60000 + ).catch(nada); let tags = JSON.parse(await redis.get(`${pubkey}:follows`)) || []; if (tagsonly) return res.send(tags); @@ -667,14 +677,22 @@ app.get("/:pubkey/follows", async (req, res) => { for (let f of tags) { let [_, pubkey] = f; - await q(`${pubkey}:profile:f1`, { + q(`${pubkey}:profile:f1`, { limit: 1, kinds: [0], authors: [pubkey] - }); + }).catch(nada); let user = JSON.parse(await redis.get(`user:${pubkey}`)); - if (user) follows.push(user); + + if (!user) + user = { + username: pubkey.substr(0, 6), + pubkey, + anon: true + }; + + follows.push(user); } follows = uniq(follows, e => e.pubkey); @@ -691,20 +709,31 @@ app.get("/:pubkey/followers", async (req, res) => { try { let { pubkey } = req.params; - let pubkeys = await got( - `${config.nostr}/followers?pubkey=${pubkey}` - ).json(); + let pubkeys = [ + ...new Set([ + ...(await got(`${config.nostr}/followers?pubkey=${pubkey}`).json()), + ...(await redis.sMembers(`${pubkey}:followers`)) + ]) + ]; let followers = []; + q( + `${pubkey}:followers`, + { kinds: [3], "#p": [pubkey] }, + 60000, + 3600, + 60000 + ).catch(nada); + for (let pubkey of pubkeys) { let user = JSON.parse(await redis.get(`user:${pubkey}`)); if (!user || !user.updated) { - await q(`${pubkey}:profile:f2`, { + q(`${pubkey}:profile:f2`, { limit: 1, kinds: [0], authors: [pubkey] - }); + }).catch(nada); user = JSON.parse(await redis.get(`user:${pubkey}`)); } -- cgit v1.2.3 From dce07aa93b3c2f47aca0c24cea6adde9ec37f1a7 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 8 Jan 2023 21:05:34 -0800 Subject: tech debt jubilee --- Caddyfile | 21 - apidoc/api_data.js | 762 ------------- apidoc/api_data.json | 742 ------------- apidoc/api_project.js | 16 - apidoc/api_project.json | 16 - apidoc/css/style.css | 525 --------- apidoc/fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes apidoc/fonts/glyphicons-halflings-regular.svg | 288 ----- apidoc/fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes apidoc/fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes apidoc/fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes apidoc/img/android-chrome-192x192.png | Bin 7672 -> 0 bytes apidoc/img/android-chrome-512x512.png | Bin 28928 -> 0 bytes apidoc/img/apple-touch-icon.png | Bin 8775 -> 0 bytes apidoc/img/favicon-16x16.png | Bin 566 -> 0 bytes apidoc/img/favicon-32x32.png | Bin 1154 -> 0 bytes apidoc/img/favicon.ico | Bin 24838 -> 0 bytes apidoc/index.html | 816 -------------- apidoc/locales/ca.js | 25 - apidoc/locales/cs.js | 25 - apidoc/locales/de.js | 25 - apidoc/locales/es.js | 25 - apidoc/locales/fr.js | 25 - apidoc/locales/it.js | 25 - apidoc/locales/locale.js | 51 - apidoc/locales/nl.js | 25 - apidoc/locales/pl.js | 25 - apidoc/locales/pt_br.js | 25 - apidoc/locales/ro.js | 25 - apidoc/locales/ru.js | 25 - apidoc/locales/tr.js | 25 - apidoc/locales/vi.js | 25 - apidoc/locales/zh.js | 25 - apidoc/locales/zh_cn.js | 27 - apidoc/main.js | 1146 -------------------- apidoc/utils/handlebars_helper.js | 416 ------- apidoc/utils/send_sample_request.js | 272 ----- apidoc/utils/send_sample_request_utils.js | 97 -- apidoc/vendor/bootstrap.min.css | 6 - apidoc/vendor/bootstrap.min.js | 6 - apidoc/vendor/diff_match_patch.min.js | 49 - apidoc/vendor/handlebars.min.js | 29 - apidoc/vendor/jquery.min.js | 2 - apidoc/vendor/list.min.js | 2 - apidoc/vendor/lodash.custom.min.js | 41 - apidoc/vendor/path-to-regexp/LICENSE | 21 - apidoc/vendor/path-to-regexp/index.js | 204 ---- apidoc/vendor/polyfill.js | 96 -- apidoc/vendor/prism.css | 125 --- apidoc/vendor/prism.js | 21 - apidoc/vendor/require.min.js | 37 - apidoc/vendor/semver.min.js | 1 - apidoc/vendor/webfontloader.js | 17 - db/db.js | 5 - db/index.js | 157 --- db/init/prodschema.sql | 312 ------ db/migrations/20210612181508_init_referrals.js | 24 - .../20210612192804_init_waiting_list.js.js | 24 - db/migrations/20210612203333_schema_diff_merges.js | 39 - .../20210625232223_add_invoice_id_to_payments.js | 13 - .../20210628221727_add_webhook_to_invoices.js | 11 - db/migrations/20210713174217_add_admin_to_users.js | 16 - db/migrations/20210714123455_data_fixes_merge.js | 22 - db/migrations/20210714123456_add_fks_merge.js | 45 - db/migrations/s2/user_adjustments.js | 67 -- db/models/accounts.js | 217 ---- db/models/codes.js | 32 - db/models/deposits.js | 76 -- db/models/invoices.js | 202 ---- db/models/keys.js | 40 - db/models/orders.js | 112 -- db/models/payments.js | 220 ---- db/models/referrals.js | 90 -- db/models/requests.js | 67 -- db/models/users.js | 270 ----- db/models/waiting_list.js | 45 - db/models/withdrawals.js | 103 -- db/normalized/README.md | 48 - db/normalized/accounts.js | 231 ---- db/normalized/bank_accounts.js | 24 - db/normalized/credits.js | 236 ---- db/normalized/debits.js | 236 ---- db/normalized/deposits.js | 100 -- db/normalized/exchanges.js | 232 ---- db/normalized/invoices.js | 206 ---- db/normalized/liquid_assets.js | 21 - db/normalized/ln_keys.js | 16 - db/normalized/lnurl_codes.js | 14 - db/normalized/non_custodial_accounts.js | 91 -- db/normalized/orders.js | 29 - db/normalized/referrals.js | 20 - db/normalized/transactions.js | 41 - db/normalized/transfers.js | 243 ----- db/normalized/user_preferences.js | 20 - db/normalized/user_security.js | 22 - db/normalized/users.js | 18 - db/normalized/vouchers.js | 18 - db/normalized/waiting_list.js | 60 - db/normalized/withdrawals.js | 128 --- db/schema.sql | 479 -------- doc/PAYMENTS.md | 52 - doc/fund-server-bitcoin-cli.md | 44 - doc/img/50-bitcoin-server-balance.gif | Bin 7791 -> 0 bytes docker-compose.yml | 224 +--- fx.sample | 1 - git_hooks/pre-commit | 11 - index.js | 30 +- k8s/app-claim0-persistentvolumeclaim.yaml | 14 - k8s/app-deployment.yaml | 44 - k8s/app-service.yaml | 19 - k8s/bitcoin-claim0-persistentvolumeclaim.yaml | 14 - k8s/bitcoin-deployment.yaml | 42 - k8s/bitcoin-service.yaml | 22 - k8s/docker-compose-resolved.yaml | 118 -- ...lectrs-liquid-claim0-persistentvolumeclaim.yaml | 14 - k8s/electrs-liquid-deployment.yaml | 62 -- k8s/electrs-liquid-service.yaml | 22 - k8s/esplora-liquid-deployment.yaml | 40 - k8s/esplora-liquid-service.yaml | 19 - k8s/la-claim0-persistentvolumeclaim.yaml | 14 - k8s/la-deployment.yaml | 42 - k8s/lb-claim0-persistentvolumeclaim.yaml | 14 - k8s/lb-deployment.yaml | 42 - k8s/liquid-claim0-persistentvolumeclaim.yaml | 14 - k8s/liquid-deployment.yaml | 44 - k8s/liquid-service.yaml | 28 - k8s/maria-claim0-persistentvolumeclaim.yaml | 14 - k8s/maria-deployment.yaml | 44 - k8s/maria-service.yaml | 19 - k8s/ui-claim0-persistentvolumeclaim.yaml | 14 - k8s/ui-deployment.yaml | 44 - k8s/ui-service.yaml | 19 - lib/account.js | 49 - lib/buildliquidtx.js | 160 --- lib/buildtx.js | 90 -- lib/countries.js | 249 ----- lib/liquid.js | 3 - lib/lnd.js | 1 - lib/logo.js | 1 - lib/nostr.js | 194 +++- lib/passport.js | 18 +- lib/persist.js | 22 - lib/rates.js | 18 +- lib/read.js | 25 - lib/register.js | 56 +- lib/rpc.js | 24 - lib/send.js | 16 +- lib/sendInternal.js | 22 - lib/sockets.js | 103 +- lib/store.js | 5 +- lib/sync.js | 414 ------- lib/utils.js | 148 +-- lib/wretch.js | 4 - mine.sh | 8 - nodemon.json | 3 - package.json | 37 +- prod.yml | 96 -- reg.yml | 96 -- routes/admin.js | 566 ---------- routes/assets.js | 536 --------- routes/bitcoin/broadcast.js | 56 - routes/bitcoin/conversionFee.js | 12 - routes/bitcoin/fee.js | 73 -- routes/bitcoin/generate.js | 5 - routes/bitcoin/index.js | 13 - routes/bitcoin/receive.js | 281 ----- routes/bitcoin/send.js | 192 ---- routes/bitcoin/sweep.js | 87 -- routes/events.js | 69 -- routes/funding.js | 96 -- routes/invoices.js | 7 +- routes/lightning/query.js | 1 - routes/lightning/receive.js | 25 - routes/liquid/broadcast.js | 58 - routes/liquid/conversionFee.js | 12 - routes/liquid/fee.js | 89 -- routes/liquid/generate.js | 5 - routes/liquid/index.js | 11 - routes/liquid/receive.js | 406 ------- routes/liquid/send.js | 276 ----- routes/lnurl.js | 3 - routes/locations.js | 12 +- routes/payments.js | 402 +++---- routes/referrals.js | 272 ----- routes/requests.js | 2 - routes/swaps.js | 587 ---------- routes/tickets.js | 22 - routes/users.js | 331 +----- sampleconfig/certbot/.gitkeep | 1 - sampleconfig/index.js | 74 +- sampleconfig/liquid/elements.conf | 18 - sampleconfig/lnd/lnd.conf | 25 - sampleconfig/lnd/pwd | 1 - sampleconfig/lnurl.js | 36 - sampleconfig/nginx/default.conf.template | 38 - sampledata/conversions.json | 2 - sampledata/emails.json | 4 - sampledata/logins.json | 1 - sampledata/payments.json | 2 - sampledata/recipients.json | 2 - sampledata/withdrawals.json | 1 - scripts/apiParse.js | 46 - scripts/check.js | 24 - scripts/purge-except-git.sh | 31 - secret | 0 205 files changed, 449 insertions(+), 18314 deletions(-) delete mode 100644 Caddyfile delete mode 100644 apidoc/api_data.js delete mode 100644 apidoc/api_data.json delete mode 100644 apidoc/api_project.js delete mode 100644 apidoc/api_project.json delete mode 100644 apidoc/css/style.css delete mode 100644 apidoc/fonts/glyphicons-halflings-regular.eot delete mode 100644 apidoc/fonts/glyphicons-halflings-regular.svg delete mode 100644 apidoc/fonts/glyphicons-halflings-regular.ttf delete mode 100644 apidoc/fonts/glyphicons-halflings-regular.woff delete mode 100644 apidoc/fonts/glyphicons-halflings-regular.woff2 delete mode 100644 apidoc/img/android-chrome-192x192.png delete mode 100644 apidoc/img/android-chrome-512x512.png delete mode 100644 apidoc/img/apple-touch-icon.png delete mode 100644 apidoc/img/favicon-16x16.png delete mode 100644 apidoc/img/favicon-32x32.png delete mode 100644 apidoc/img/favicon.ico delete mode 100644 apidoc/index.html delete mode 100644 apidoc/locales/ca.js delete mode 100644 apidoc/locales/cs.js delete mode 100644 apidoc/locales/de.js delete mode 100644 apidoc/locales/es.js delete mode 100644 apidoc/locales/fr.js delete mode 100644 apidoc/locales/it.js delete mode 100644 apidoc/locales/locale.js delete mode 100644 apidoc/locales/nl.js delete mode 100644 apidoc/locales/pl.js delete mode 100644 apidoc/locales/pt_br.js delete mode 100644 apidoc/locales/ro.js delete mode 100644 apidoc/locales/ru.js delete mode 100644 apidoc/locales/tr.js delete mode 100644 apidoc/locales/vi.js delete mode 100644 apidoc/locales/zh.js delete mode 100644 apidoc/locales/zh_cn.js delete mode 100644 apidoc/main.js delete mode 100644 apidoc/utils/handlebars_helper.js delete mode 100644 apidoc/utils/send_sample_request.js delete mode 100644 apidoc/utils/send_sample_request_utils.js delete mode 100644 apidoc/vendor/bootstrap.min.css delete mode 100644 apidoc/vendor/bootstrap.min.js delete mode 100644 apidoc/vendor/diff_match_patch.min.js delete mode 100644 apidoc/vendor/handlebars.min.js delete mode 100644 apidoc/vendor/jquery.min.js delete mode 100644 apidoc/vendor/list.min.js delete mode 100644 apidoc/vendor/lodash.custom.min.js delete mode 100644 apidoc/vendor/path-to-regexp/LICENSE delete mode 100644 apidoc/vendor/path-to-regexp/index.js delete mode 100644 apidoc/vendor/polyfill.js delete mode 100644 apidoc/vendor/prism.css delete mode 100644 apidoc/vendor/prism.js delete mode 100644 apidoc/vendor/require.min.js delete mode 100644 apidoc/vendor/semver.min.js delete mode 100644 apidoc/vendor/webfontloader.js delete mode 100644 db/db.js delete mode 100644 db/index.js delete mode 100644 db/init/prodschema.sql delete mode 100644 db/migrations/20210612181508_init_referrals.js delete mode 100644 db/migrations/20210612192804_init_waiting_list.js.js delete mode 100644 db/migrations/20210612203333_schema_diff_merges.js delete mode 100644 db/migrations/20210625232223_add_invoice_id_to_payments.js delete mode 100644 db/migrations/20210628221727_add_webhook_to_invoices.js delete mode 100644 db/migrations/20210713174217_add_admin_to_users.js delete mode 100644 db/migrations/20210714123455_data_fixes_merge.js delete mode 100644 db/migrations/20210714123456_add_fks_merge.js delete mode 100644 db/migrations/s2/user_adjustments.js delete mode 100644 db/models/accounts.js delete mode 100644 db/models/codes.js delete mode 100644 db/models/deposits.js delete mode 100644 db/models/invoices.js delete mode 100644 db/models/keys.js delete mode 100644 db/models/orders.js delete mode 100644 db/models/payments.js delete mode 100644 db/models/referrals.js delete mode 100644 db/models/requests.js delete mode 100644 db/models/users.js delete mode 100644 db/models/waiting_list.js delete mode 100644 db/models/withdrawals.js delete mode 100644 db/normalized/README.md delete mode 100644 db/normalized/accounts.js delete mode 100644 db/normalized/bank_accounts.js delete mode 100644 db/normalized/credits.js delete mode 100644 db/normalized/debits.js delete mode 100644 db/normalized/deposits.js delete mode 100644 db/normalized/exchanges.js delete mode 100644 db/normalized/invoices.js delete mode 100644 db/normalized/liquid_assets.js delete mode 100644 db/normalized/ln_keys.js delete mode 100644 db/normalized/lnurl_codes.js delete mode 100644 db/normalized/non_custodial_accounts.js delete mode 100644 db/normalized/orders.js delete mode 100644 db/normalized/referrals.js delete mode 100644 db/normalized/transactions.js delete mode 100644 db/normalized/transfers.js delete mode 100644 db/normalized/user_preferences.js delete mode 100644 db/normalized/user_security.js delete mode 100644 db/normalized/users.js delete mode 100644 db/normalized/vouchers.js delete mode 100644 db/normalized/waiting_list.js delete mode 100644 db/normalized/withdrawals.js delete mode 100644 db/schema.sql delete mode 100644 doc/PAYMENTS.md delete mode 100644 doc/fund-server-bitcoin-cli.md delete mode 100644 doc/img/50-bitcoin-server-balance.gif delete mode 100644 fx.sample delete mode 100644 git_hooks/pre-commit delete mode 100644 k8s/app-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/app-deployment.yaml delete mode 100644 k8s/app-service.yaml delete mode 100644 k8s/bitcoin-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/bitcoin-deployment.yaml delete mode 100644 k8s/bitcoin-service.yaml delete mode 100644 k8s/docker-compose-resolved.yaml delete mode 100644 k8s/electrs-liquid-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/electrs-liquid-deployment.yaml delete mode 100644 k8s/electrs-liquid-service.yaml delete mode 100644 k8s/esplora-liquid-deployment.yaml delete mode 100644 k8s/esplora-liquid-service.yaml delete mode 100644 k8s/la-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/la-deployment.yaml delete mode 100644 k8s/lb-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/lb-deployment.yaml delete mode 100644 k8s/liquid-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/liquid-deployment.yaml delete mode 100644 k8s/liquid-service.yaml delete mode 100644 k8s/maria-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/maria-deployment.yaml delete mode 100644 k8s/maria-service.yaml delete mode 100644 k8s/ui-claim0-persistentvolumeclaim.yaml delete mode 100644 k8s/ui-deployment.yaml delete mode 100644 k8s/ui-service.yaml delete mode 100644 lib/account.js delete mode 100644 lib/buildliquidtx.js delete mode 100644 lib/buildtx.js delete mode 100644 lib/countries.js delete mode 100644 lib/liquid.js delete mode 100644 lib/lnd.js delete mode 100644 lib/logo.js delete mode 100644 lib/persist.js delete mode 100644 lib/read.js delete mode 100644 lib/rpc.js delete mode 100644 lib/sync.js delete mode 100644 lib/wretch.js delete mode 100755 mine.sh delete mode 100644 nodemon.json delete mode 100644 prod.yml delete mode 100644 reg.yml delete mode 100644 routes/admin.js delete mode 100644 routes/assets.js delete mode 100644 routes/bitcoin/broadcast.js delete mode 100644 routes/bitcoin/conversionFee.js delete mode 100644 routes/bitcoin/fee.js delete mode 100644 routes/bitcoin/generate.js delete mode 100644 routes/bitcoin/index.js delete mode 100644 routes/bitcoin/receive.js delete mode 100644 routes/bitcoin/send.js delete mode 100644 routes/bitcoin/sweep.js delete mode 100644 routes/funding.js delete mode 100644 routes/liquid/broadcast.js delete mode 100644 routes/liquid/conversionFee.js delete mode 100644 routes/liquid/fee.js delete mode 100644 routes/liquid/generate.js delete mode 100644 routes/liquid/index.js delete mode 100644 routes/liquid/receive.js delete mode 100644 routes/liquid/send.js delete mode 100644 routes/referrals.js delete mode 100644 routes/swaps.js delete mode 100644 routes/tickets.js delete mode 100644 sampleconfig/certbot/.gitkeep delete mode 100644 sampleconfig/liquid/elements.conf delete mode 100644 sampleconfig/lnd/lnd.conf delete mode 100644 sampleconfig/lnd/pwd delete mode 100644 sampleconfig/lnurl.js delete mode 100644 sampleconfig/nginx/default.conf.template delete mode 100644 sampledata/conversions.json delete mode 100644 sampledata/emails.json delete mode 100644 sampledata/logins.json delete mode 100644 sampledata/payments.json delete mode 100644 sampledata/recipients.json delete mode 100644 sampledata/withdrawals.json delete mode 100644 scripts/apiParse.js delete mode 100644 scripts/check.js delete mode 100755 scripts/purge-except-git.sh delete mode 100644 secret (limited to 'db') diff --git a/Caddyfile b/Caddyfile deleted file mode 100644 index 3e41ca11..00000000 --- a/Caddyfile +++ /dev/null @@ -1,21 +0,0 @@ -:80 { - handle_path /api* { - reverse_proxy app:3119 - } - - handle_path /.well-known* { - reverse_proxy app:3119 - } - - handle /lnurl* { - reverse_proxy app:3118 - } - - handle /ws* { - reverse_proxy app:3119 - } - - handle { - reverse_proxy ui:3000 - } -} diff --git a/apidoc/api_data.js b/apidoc/api_data.js deleted file mode 100644 index 44a2a9c4..00000000 --- a/apidoc/api_data.js +++ /dev/null @@ -1,762 +0,0 @@ -define({ - api: [ - { - type: "get", - url: "/accounts", - title: "Retrieve list of accounts", - name: "accounts", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of accounts

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "accounts" : [\n { "user": \'Adam\', "account_id": \'123\', balance: 500 ... },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/deposits", - title: "Retrieve summary of deposits", - name: "deposits", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of deposits

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "deposits" : [\n { "seller": \'Adam\', "buyer": \'bob\' ... },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/users", - title: "Retrieve user list", - name: "getUsers", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "Object", - optional: false, - field: "users", - description: "

List of users

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "users" : [\n { "username": \'John Doe\', "email": \'johndoe@gmail.com\', ...},\n { "username": \'Jane Doe\', "email": \'janedoe@gmail.com\', ...}\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/invoices", - title: "Retrieve summary of invoices", - name: "invoices", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of invoices

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "invoices" : [\n { "seller": \'Adam\', "buyer": \'bob\' ... },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/orders", - title: "Retrieve summary of orders", - name: "orders", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of orders

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "orders" : [\n { "seller": \'Adam\', "buyer": \'bob\' ... },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/payments", - title: "Retrieve summary of payments", - name: "payments", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of payments

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "payments" : [\n { "seller": \'Adam\', "buyer": \'bob\' ... },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/referrals", - title: "Retrieve list of referral tokens", - name: "referrals", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of referral tokens

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - "HTTP/1.1 200 OK\n{\n \"referrals\" : [\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername1', expiry: null },\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername2', expiry: null },\n ]\n}", - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/referrals", - title: "Retrieve list of referral tokens", - name: "referrals", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of referral tokens

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - "HTTP/1.1 200 OK\n{\n \"referrals\" : [\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername1', expiry: null },\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername2', expiry: null },\n ]\n}", - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/user_kyc", - title: "Retrieve user kyc details", - name: "user_kyc", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of kyc details for each user

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "users" : [\n { "username": \'Adam\', "email": \'adam@gmail.com\', ... kyc_data... },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/transactions", - title: "Retrieve summary of user transactions", - name: "user_transactions", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of invoices, payments, orders for users

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "transactions" : [\n { "username": \'Adam\', "email": \'adam@gmail.com\', invoices: 1, orders: 0, payments: 2 },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/withdrawals", - title: "Retrieve summary of withdrawals", - name: "withdrawals", - group: "Admin", - permission: [ - { - name: "admin" - } - ], - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "returns", - description: "

list of withdrawals

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "withdrawals" : [\n { "seller": \'Adam\', "buyer": \'bob\' ... },\n ...\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/admin.js", - groupTitle: "Admin" - }, - { - type: "get", - url: "/info", - title: "Request server information", - name: "GetInfo", - group: "Info", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "Object", - optional: false, - field: "fx", - description: - "

Exchange rates relative to USD - fx.CUR is the value of one CUR in USD.

" - }, - { - group: "Success 200", - type: "String[]", - optional: false, - field: "networks", - description: - "

Array of supported networks; possible values are "bitcoin", "liquid" and "lightning".

" - }, - { - group: "Success 200", - type: "String", - optional: false, - field: "clientVersion", - description: "

the current git commit of the ui

" - } - ] - } - }, - version: "0.0.0", - filename: "routes/info.js", - groupTitle: "Info" - }, - { - type: "post", - url: "/grant", - title: "Grant Referral code", - name: "Grant", - group: "Referrals", - permission: [ - { - name: "member" - } - ], - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "sponsor_id", - optional: false, - field: "user_id", - description: "

of member generating referral code.

" - }, - { - group: "Parameter", - type: "expiry", - optional: false, - field: "optional", - description: "

expiry date for token.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "token", - description: "

referral token

" - }, - { - group: "Success 200", - type: "Date", - optional: false, - field: "expiry", - description: "

optional expiry

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "token": "*********", \n "status": "available", \n "expiry": null\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/referrals.js", - groupTitle: "Referrals" - }, - { - type: "get", - url: "/grant", - title: "Grant Referral code (variation using get)", - name: "Grant", - group: "Referrals", - permission: [ - { - name: "member" - }, - { - name: "member" - } - ], - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "sponsor_id", - optional: false, - field: "user_id", - description: "

of member generating referral code.

" - }, - { - group: "Parameter", - type: "expiry", - optional: false, - field: "optional", - description: "

expiry date for token.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "token", - description: "

referral token

" - }, - { - group: "Success 200", - type: "Status", - optional: false, - field: "status", - description: "

referral status

" - }, - { - group: "Success 200", - type: "Date", - optional: false, - field: "expiry", - description: "

optional expiry

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "token": "*********", \n "status": "available", \n "expiry": null\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/referrals.js", - groupTitle: "Referrals" - }, - { - type: "get", - url: "/checkTokens/:sponsor_id", - title: "Retrieve list of referral tokens generated by this user", - name: "checkTokens", - group: "Referrals", - permission: [ - { - name: "member" - } - ], - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "sponsor_id", - optional: false, - field: "user_id", - description: "

of member generating referral code.

" - }, - { - group: "Parameter", - type: "status", - optional: false, - field: "optional", - description: "

token status.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "tokens", - description: "

array of token objects

" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "tokens": [\n {"token": "*********", "status": "available", "expiry": null}\n ]\n}', - type: "json" - } - ] - }, - version: "0.0.0", - filename: "routes/referrals.js", - groupTitle: "Referrals" - }, - { - type: "get", - url: "/isReferred/:user_id", - title: "Check if user is referred", - name: "isReferred", - group: "Referrals", - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "user_id", - optional: false, - field: "user", - description: "

id

" - } - ] - } - }, - success: { - examples: [ - { - title: "Success-Response:", - content: "HTTP/1.1 200 OK\n{\n true\n}", - type: "json" - } - ] - }, - description: - "

returns boolean indicating if user is referred or not

", - version: "0.0.0", - filename: "routes/referrals.js", - groupTitle: "Referrals" - }, - { - type: "post", - url: "/joinQueue", - title: "Join waiting list (track email & phone)", - name: "joinQueue", - group: "Referrals", - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "email", - optional: false, - field: "email", - description: "

address

" - }, - { - group: "Parameter", - type: "phone", - optional: false, - field: "phone", - description: "

number

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "verified", - defaultValue: "true", - description: "" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK\n{\n "success": true\n "message": \'Added to waiting list\'\n}', - type: "json" - } - ] - }, - description: "

referral token is updated with existing user_id

", - version: "0.0.0", - filename: "routes/referrals.js", - groupTitle: "Referrals" - }, - { - type: "get", - url: "/verify/:user_id", - title: "Verify token and apply to existing user", - name: "verify", - group: "Referrals", - permission: [ - { - name: "member" - } - ], - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "user_id", - optional: false, - field: "Current", - description: "

user

" - }, - { - group: "Parameter", - type: "token", - optional: false, - field: "Token", - description: "

to validate

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - optional: false, - field: "verified", - defaultValue: "true", - description: "" - } - ] - }, - examples: [ - { - title: "Success-Response:", - content: 'HTTP/1.1 200 OK\n{\n "verified": true\n}', - type: "json" - } - ] - }, - description: "

referral token is updated with existing user_id

", - version: "0.0.0", - filename: "routes/referrals.js", - groupTitle: "Referrals" - } - ] -}); diff --git a/apidoc/api_data.json b/apidoc/api_data.json deleted file mode 100644 index 5bab3ee7..00000000 --- a/apidoc/api_data.json +++ /dev/null @@ -1,742 +0,0 @@ -[ - { - "type": "get", - "url": "/accounts", - "title": "Retrieve list of accounts", - "name": "accounts", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of accounts

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"accounts\" : [\n { \"user\": 'Adam', \"account_id\": '123', balance: 500 ... },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/deposits", - "title": "Retrieve summary of deposits", - "name": "deposits", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of deposits

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"deposits\" : [\n { \"seller\": 'Adam', \"buyer\": 'bob' ... },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/users", - "title": "Retrieve user list", - "name": "getUsers", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "users", - "description": "

List of users

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"users\" : [\n { \"username\": 'John Doe', \"email\": 'johndoe@gmail.com', ...},\n { \"username\": 'Jane Doe', \"email\": 'janedoe@gmail.com', ...}\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/invoices", - "title": "Retrieve summary of invoices", - "name": "invoices", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of invoices

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"invoices\" : [\n { \"seller\": 'Adam', \"buyer\": 'bob' ... },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/orders", - "title": "Retrieve summary of orders", - "name": "orders", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of orders

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"orders\" : [\n { \"seller\": 'Adam', \"buyer\": 'bob' ... },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/payments", - "title": "Retrieve summary of payments", - "name": "payments", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of payments

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"payments\" : [\n { \"seller\": 'Adam', \"buyer\": 'bob' ... },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/referrals", - "title": "Retrieve list of referral tokens", - "name": "referrals", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of referral tokens

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"referrals\" : [\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername1', expiry: null },\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername2', expiry: null },\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/referrals", - "title": "Retrieve list of referral tokens", - "name": "referrals", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of referral tokens

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"referrals\" : [\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername1', expiry: null },\n { \"token\": '***', \"status\": 'pending', sponsor: 'Adam', user: 'newUsername2', expiry: null },\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/user_kyc", - "title": "Retrieve user kyc details", - "name": "user_kyc", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of kyc details for each user

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"users\" : [\n { \"username\": 'Adam', \"email\": 'adam@gmail.com', ... kyc_data... },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/transactions", - "title": "Retrieve summary of user transactions", - "name": "user_transactions", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of invoices, payments, orders for users

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"transactions\" : [\n { \"username\": 'Adam', \"email\": 'adam@gmail.com', invoices: 1, orders: 0, payments: 2 },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/withdrawals", - "title": "Retrieve summary of withdrawals", - "name": "withdrawals", - "group": "Admin", - "permission": [ - { - "name": "admin" - } - ], - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "returns", - "description": "

list of withdrawals

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"withdrawals\" : [\n { \"seller\": 'Adam', \"buyer\": 'bob' ... },\n ...\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/admin.js", - "groupTitle": "Admin" - }, - { - "type": "get", - "url": "/info", - "title": "Request server information", - "name": "GetInfo", - "group": "Info", - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "fx", - "description": "

Exchange rates relative to USD - fx.CUR is the value of one CUR in USD.

" - }, - { - "group": "Success 200", - "type": "String[]", - "optional": false, - "field": "networks", - "description": "

Array of supported networks; possible values are "bitcoin", "liquid" and "lightning".

" - }, - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "clientVersion", - "description": "

the current git commit of the ui

" - } - ] - } - }, - "version": "0.0.0", - "filename": "routes/info.js", - "groupTitle": "Info" - }, - { - "type": "post", - "url": "/grant", - "title": "Grant Referral code", - "name": "Grant", - "group": "Referrals", - "permission": [ - { - "name": "member" - } - ], - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "sponsor_id", - "optional": false, - "field": "user_id", - "description": "

of member generating referral code.

" - }, - { - "group": "Parameter", - "type": "expiry", - "optional": false, - "field": "optional", - "description": "

expiry date for token.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "token", - "description": "

referral token

" - }, - { - "group": "Success 200", - "type": "Date", - "optional": false, - "field": "expiry", - "description": "

optional expiry

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"token\": \"*********\", \n \"status\": \"available\", \n \"expiry\": null\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/referrals.js", - "groupTitle": "Referrals" - }, - { - "type": "get", - "url": "/grant", - "title": "Grant Referral code (variation using get)", - "name": "Grant", - "group": "Referrals", - "permission": [ - { - "name": "member" - }, - { - "name": "member" - } - ], - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "sponsor_id", - "optional": false, - "field": "user_id", - "description": "

of member generating referral code.

" - }, - { - "group": "Parameter", - "type": "expiry", - "optional": false, - "field": "optional", - "description": "

expiry date for token.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "type": "String", - "optional": false, - "field": "token", - "description": "

referral token

" - }, - { - "group": "Success 200", - "type": "Status", - "optional": false, - "field": "status", - "description": "

referral status

" - }, - { - "group": "Success 200", - "type": "Date", - "optional": false, - "field": "expiry", - "description": "

optional expiry

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"token\": \"*********\", \n \"status\": \"available\", \n \"expiry\": null\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/referrals.js", - "groupTitle": "Referrals" - }, - { - "type": "get", - "url": "/checkTokens/:sponsor_id", - "title": "Retrieve list of referral tokens generated by this user", - "name": "checkTokens", - "group": "Referrals", - "permission": [ - { - "name": "member" - } - ], - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "sponsor_id", - "optional": false, - "field": "user_id", - "description": "

of member generating referral code.

" - }, - { - "group": "Parameter", - "type": "status", - "optional": false, - "field": "optional", - "description": "

token status.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "tokens", - "description": "

array of token objects

" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"tokens\": [\n {\"token\": \"*********\", \"status\": \"available\", \"expiry\": null}\n ]\n}", - "type": "json" - } - ] - }, - "version": "0.0.0", - "filename": "routes/referrals.js", - "groupTitle": "Referrals" - }, - { - "type": "get", - "url": "/isReferred/:user_id", - "title": "Check if user is referred", - "name": "isReferred", - "group": "Referrals", - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "user_id", - "optional": false, - "field": "user", - "description": "

id

" - } - ] - } - }, - "success": { - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n true\n}", - "type": "json" - } - ] - }, - "description": "

returns boolean indicating if user is referred or not

", - "version": "0.0.0", - "filename": "routes/referrals.js", - "groupTitle": "Referrals" - }, - { - "type": "post", - "url": "/joinQueue", - "title": "Join waiting list (track email & phone)", - "name": "joinQueue", - "group": "Referrals", - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "email", - "optional": false, - "field": "email", - "description": "

address

" - }, - { - "group": "Parameter", - "type": "phone", - "optional": false, - "field": "phone", - "description": "

number

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "verified", - "defaultValue": "true", - "description": "" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"success\": true\n \"message\": 'Added to waiting list'\n}", - "type": "json" - } - ] - }, - "description": "

referral token is updated with existing user_id

", - "version": "0.0.0", - "filename": "routes/referrals.js", - "groupTitle": "Referrals" - }, - { - "type": "get", - "url": "/verify/:user_id", - "title": "Verify token and apply to existing user", - "name": "verify", - "group": "Referrals", - "permission": [ - { - "name": "member" - } - ], - "parameter": { - "fields": { - "Parameter": [ - { - "group": "Parameter", - "type": "user_id", - "optional": false, - "field": "Current", - "description": "

user

" - }, - { - "group": "Parameter", - "type": "token", - "optional": false, - "field": "Token", - "description": "

to validate

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [ - { - "group": "Success 200", - "optional": false, - "field": "verified", - "defaultValue": "true", - "description": "" - } - ] - }, - "examples": [ - { - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK\n{\n \"verified\": true\n}", - "type": "json" - } - ] - }, - "description": "

referral token is updated with existing user_id

", - "version": "0.0.0", - "filename": "routes/referrals.js", - "groupTitle": "Referrals" - } -] diff --git a/apidoc/api_project.js b/apidoc/api_project.js deleted file mode 100644 index 3c0eb6aa..00000000 --- a/apidoc/api_project.js +++ /dev/null @@ -1,16 +0,0 @@ -define({ - title: "CoinOS Server API Documentation", - url: "https://coinos.io/api", - name: "coinos", - version: "0.0.1", - description: "", - sampleUrl: false, - defaultVersion: "0.0.0", - apidoc: "0.3.0", - generator: { - name: "apidoc", - time: "2021-08-18T19:13:24.486Z", - url: "https://apidocjs.com", - version: "0.29.0" - } -}); diff --git a/apidoc/api_project.json b/apidoc/api_project.json deleted file mode 100644 index 42943876..00000000 --- a/apidoc/api_project.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "title": "CoinOS Server API Documentation", - "url": "https://coinos.io/api", - "name": "coinos", - "version": "0.0.1", - "description": "", - "sampleUrl": false, - "defaultVersion": "0.0.0", - "apidoc": "0.3.0", - "generator": { - "name": "apidoc", - "time": "2021-08-18T19:13:24.486Z", - "url": "https://apidocjs.com", - "version": "0.29.0" - } -} diff --git a/apidoc/css/style.css b/apidoc/css/style.css deleted file mode 100644 index 9f3c88ac..00000000 --- a/apidoc/css/style.css +++ /dev/null @@ -1,525 +0,0 @@ -/* ------------------------------------------------------------------------------------------ - * Content - * ------------------------------------------------------------------------------------------ */ -body { - max-width: 1280px; -} - -body, p, a, div, th, td { - font-family: "Source Sans Pro", sans-serif; - font-weight: 400; - font-size: 16px; -} - -@media (min-width: 1200px) { - body.container-fluid { - padding-right: 0px; - padding-left: 0px; - margin-right: 0px; - margin-left: 0px; - } -} - -td.code { - font-size: 14px; - font-family: "Source Code Pro", monospace; - font-style: normal; - font-weight: 400; -} - -#content { - padding-top: 16px; - z-Index: -1; - margin-left: 270px; -} - -p { - color: #808080; -} - -h1 { - font-family: "Source Sans Pro Semibold", sans-serif; - font-weight: normal; - font-size: 44px; - line-height: 50px; - margin: 0 0 10px 0; - padding: 0; -} - -h2 { - font-family: "Source Sans Pro", sans-serif; - font-weight: normal; - font-size: 24px; - line-height: 40px; - margin: 0 0 20px 0; - padding: 0; -} - -section { - border-top: 1px solid #ebebeb; - padding: 30px 0; -} - -section h1 { - font-family: "Source Sans Pro", sans-serif; - font-weight: 700; - font-size: 32px; - line-height: 40px; - margin: 0 0 20px 0; - padding: 0; -} - -article { - padding: 14px 0 30px 0; -} - -article h1 { - font-family: "Source Sans Pro Bold", sans-serif; - font-weight: 600; - font-size: 24px; - line-height: 26px; -} - -article h2 { - font-family: "Source Sans Pro", sans-serif; - font-weight: 600; - font-size: 18px; - line-height: 24px; - margin: 0 0 10px 0; -} - -article h3 { - font-family: "Source Sans Pro", sans-serif; - font-weight: 600; - font-size: 16px; - line-height: 18px; - margin: 0 0 10px 0; -} - -article h4 { - font-family: "Source Sans Pro", sans-serif; - font-weight: 600; - font-size: 14px; - line-height: 16px; - margin: 0 0 8px 0; -} - -table { - border-collapse: collapse; - width: 100%; - margin: 0 0 20px 0; -} - -th { - background-color: #f5f5f5; - text-align: left; - font-family: "Source Sans Pro", sans-serif; - font-weight: 700; - padding: 4px 8px; - border: #e0e0e0 1px solid; -} - -td { - vertical-align: top; - padding: 10px 8px 0 8px; - border: #e0e0e0 1px solid; -} - -#generator .content { - color: #b0b0b0; - border-top: 1px solid #ebebeb; - padding: 10px 0; -} - -.label-optional { - float: right; - background-color: grey; - margin-top: 4px; - user-select: none; -} - -.open-left { - right: 0; - left: auto; -} - -/* ------------------------------------------------------------------------------------------ - * apidoc - intro - * ------------------------------------------------------------------------------------------ */ - -#apidoc .apidoc { - border-top: 1px solid #ebebeb; - padding: 30px 0; -} - -#apidoc h1 { - font-family: "Source Sans Pro", sans-serif; - font-weight: 700; - font-size: 32px; - line-height: 40px; - margin: 0 0 20px 0; - padding: 0; -} - -#apidoc h2 { - font-family: "Source Sans Pro Bold", sans-serif; - font-weight: 600; - font-size: 22px; - line-height: 26px; - padding-top: 14px; -} - -/* ------------------------------------------------------------------------------------------ - * Request type - * ------------------------------------------------------------------------------------------ */ -.type { - font-family: "Source Sans Pro", sans-serif; - font-weight: 600; - font-size: 15px; - display: inline-block; - margin: 0 0 5px 0; - padding: 4px 5px; - border-radius: 6px; - text-transform: uppercase; - background-color: #3387CC; - color: #ffffff; -} - -.type__get { - background-color: green; -} - -.type__put { - background-color: #e5c500; -} - -.type__post { - background-color: #4070ec; -} - -.type__delete { - background-color: #ed0039; -} - -/* ------------------------------------------------------------------------------------------ - * Sidenav - * ------------------------------------------------------------------------------------------ */ -.sidenav { - width: 228px; - margin: 0; - padding: 0 20px 20px 20px; - position: fixed; - top: 50px; - left: 0; - bottom: 0; - overflow-x: hidden; - overflow-y: auto; - background-color: #f5f5f5; - z-index: 10; -} - -.sidenav > li > a { - display: block; - width: 192px; - margin: 0; - padding: 2px 11px; - border: 0; - border-left: transparent 4px solid; - border-right: transparent 4px solid; - font-family: "Source Sans Pro", sans-serif; - font-weight: 400; - font-size: 14px; -} - -.sidenav > li.nav-header { - margin-top: 8px; - margin-bottom: 8px; -} - -.sidenav > li.nav-header > a { - padding: 5px 15px; - border: 1px solid #e5e5e5; - width: 190px; - font-family: "Source Sans Pro", sans-serif; - font-weight: 700; - font-size: 16px; - background-color: #ffffff; -} - -.sidenav > li.active > a { - position: relative; - z-index: 2; - background-color: #0088cc; - color: #ffffff; -} - -.sidenav > li.has-modifications a { - border-right: #60d060 4px solid; -} - -.sidenav > li.is-new a { - border-left: #e5e5e5 4px solid; -} - -/* ------------------------------------------------------------------------------------------ - * Side nav search - * ------------------------------------------------------------------------------------------ */ -.sidenav-search { - width: 228px; - left: 0px; - position: fixed; - padding: 16px 20px 10px 20px; - background-color: #F5F5F5; - z-index: 11; -} - -.sidenav-search .search { - height: 26px; -} - -.search-reset { - position: absolute; - display: block; - cursor: pointer; - width: 20px; - height: 20px; - text-align: center; - right: 28px; - top: 17px; - background-color: #fff; -} - -/* ------------------------------------------------------------------------------------------ - * Compare - * ------------------------------------------------------------------------------------------ */ - -ins { - background: #60d060; - text-decoration: none; - color: #000000; -} - -del { - background: #f05050; - color: #000000; -} - -.label-ins { - background-color: #60d060; -} - -.label-del { - background-color: #f05050; - text-decoration: line-through; -} - -pre.ins { - background-color: #60d060; -} - -pre.del { - background-color: #f05050; - text-decoration: line-through; -} - -table.ins th, -table.ins td { - background-color: #60d060; -} - -table.del th, -table.del td { - background-color: #f05050; - text-decoration: line-through; -} - -tr.ins td { - background-color: #60d060; -} - -tr.del td { - background-color: #f05050; - text-decoration: line-through; -} - -/* ------------------------------------------------------------------------------------------ - * Spinner - * ------------------------------------------------------------------------------------------ */ - -#loader { - position: absolute; - width: 100%; -} - -#loader p { - padding-top: 80px; - margin-left: -4px; -} - -.spinner { - margin: 200px auto; - width: 60px; - height: 60px; - position: relative; -} - -.container1 > div, .container2 > div, .container3 > div { - width: 14px; - height: 14px; - background-color: #0088cc; - - border-radius: 100%; - position: absolute; - -webkit-animation: bouncedelay 1.2s infinite ease-in-out; - animation: bouncedelay 1.2s infinite ease-in-out; - /* Prevent first frame from flickering when animation starts */ - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} - -.spinner .spinner-container { - position: absolute; - width: 100%; - height: 100%; -} - -.container2 { - -webkit-transform: rotateZ(45deg); - transform: rotateZ(45deg); -} - -.container3 { - -webkit-transform: rotateZ(90deg); - transform: rotateZ(90deg); -} - -.circle1 { top: 0; left: 0; } -.circle2 { top: 0; right: 0; } -.circle3 { right: 0; bottom: 0; } -.circle4 { left: 0; bottom: 0; } - -.container2 .circle1 { - -webkit-animation-delay: -1.1s; - animation-delay: -1.1s; -} - -.container3 .circle1 { - -webkit-animation-delay: -1.0s; - animation-delay: -1.0s; -} - -.container1 .circle2 { - -webkit-animation-delay: -0.9s; - animation-delay: -0.9s; -} - -.container2 .circle2 { - -webkit-animation-delay: -0.8s; - animation-delay: -0.8s; -} - -.container3 .circle2 { - -webkit-animation-delay: -0.7s; - animation-delay: -0.7s; -} - -.container1 .circle3 { - -webkit-animation-delay: -0.6s; - animation-delay: -0.6s; -} - -.container2 .circle3 { - -webkit-animation-delay: -0.5s; - animation-delay: -0.5s; -} - -.container3 .circle3 { - -webkit-animation-delay: -0.4s; - animation-delay: -0.4s; -} - -.container1 .circle4 { - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} - -.container2 .circle4 { - -webkit-animation-delay: -0.2s; - animation-delay: -0.2s; -} - -.container3 .circle4 { - -webkit-animation-delay: -0.1s; - animation-delay: -0.1s; -} - -@-webkit-keyframes bouncedelay { - 0%, 80%, 100% { -webkit-transform: scale(0.0) } - 40% { -webkit-transform: scale(1.0) } -} - -@keyframes bouncedelay { - 0%, 80%, 100% { - transform: scale(0.0); - -webkit-transform: scale(0.0); - } 40% { - transform: scale(1.0); - -webkit-transform: scale(1.0); - } -} - -/* ------------------------------------------------------------------------------------------ - * Tabs - * ------------------------------------------------------------------------------------------ */ -ul.nav-tabs { - margin: 0; -} - -p.deprecated span{ - color: #ff0000; - font-weight: bold; - text-decoration: underline; -} - -/* ------------------------------------------------------------------------------------------ - * Print - * ------------------------------------------------------------------------------------------ */ - -@media print { - - #sidenav, - #version, - #versions, - section .version, - section .versions { - display: none; - } - - #content { - margin-left: 0; - } - - a { - text-decoration: none; - color: inherit; - } - - a:after { - content: " [" attr(href) "] "; - } - - p { - color: #000000 - } - - pre { - background-color: #ffffff; - color: #000000; - padding: 10px; - border: #808080 1px solid; - border-radius: 6px; - position: relative; - margin: 10px 0 20px 0; - } - -} /* /@media print */ diff --git a/apidoc/fonts/glyphicons-halflings-regular.eot b/apidoc/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a4953..00000000 Binary files a/apidoc/fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/apidoc/fonts/glyphicons-halflings-regular.svg b/apidoc/fonts/glyphicons-halflings-regular.svg deleted file mode 100644 index 94fb5490..00000000 --- a/apidoc/fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apidoc/fonts/glyphicons-halflings-regular.ttf b/apidoc/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc60..00000000 Binary files a/apidoc/fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/apidoc/fonts/glyphicons-halflings-regular.woff b/apidoc/fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 9e612858..00000000 Binary files a/apidoc/fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/apidoc/fonts/glyphicons-halflings-regular.woff2 b/apidoc/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b54..00000000 Binary files a/apidoc/fonts/glyphicons-halflings-regular.woff2 and /dev/null differ diff --git a/apidoc/img/android-chrome-192x192.png b/apidoc/img/android-chrome-192x192.png deleted file mode 100644 index 5c3d085d..00000000 Binary files a/apidoc/img/android-chrome-192x192.png and /dev/null differ diff --git a/apidoc/img/android-chrome-512x512.png b/apidoc/img/android-chrome-512x512.png deleted file mode 100644 index 2381b094..00000000 Binary files a/apidoc/img/android-chrome-512x512.png and /dev/null differ diff --git a/apidoc/img/apple-touch-icon.png b/apidoc/img/apple-touch-icon.png deleted file mode 100644 index 10a12dcc..00000000 Binary files a/apidoc/img/apple-touch-icon.png and /dev/null differ diff --git a/apidoc/img/favicon-16x16.png b/apidoc/img/favicon-16x16.png deleted file mode 100644 index 8d7a6dc8..00000000 Binary files a/apidoc/img/favicon-16x16.png and /dev/null differ diff --git a/apidoc/img/favicon-32x32.png b/apidoc/img/favicon-32x32.png deleted file mode 100644 index 361d8bce..00000000 Binary files a/apidoc/img/favicon-32x32.png and /dev/null differ diff --git a/apidoc/img/favicon.ico b/apidoc/img/favicon.ico deleted file mode 100644 index 8cc49dd4..00000000 Binary files a/apidoc/img/favicon.ico and /dev/null differ diff --git a/apidoc/index.html b/apidoc/index.html deleted file mode 100644 index 88b0f86d..00000000 --- a/apidoc/index.html +++ /dev/null @@ -1,816 +0,0 @@ - - - - - Loading... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- -
- -
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-

Loading...

-
-
- - - - diff --git a/apidoc/locales/ca.js b/apidoc/locales/ca.js deleted file mode 100644 index 65af5df2..00000000 --- a/apidoc/locales/ca.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - ca: { - 'Allowed values:' : 'Valors permesos:', - 'Compare all with predecessor': 'Comparar tot amb versió anterior', - 'compare changes to:' : 'comparar canvis amb:', - 'compared to' : 'comparat amb', - 'Default value:' : 'Valor per defecte:', - 'Description' : 'Descripció', - 'Field' : 'Camp', - 'General' : 'General', - 'Generated with' : 'Generat amb', - 'Name' : 'Nom', - 'No response values.' : 'Sense valors en la resposta.', - 'optional' : 'opcional', - 'Parameter' : 'Paràmetre', - 'Permission:' : 'Permisos:', - 'Response' : 'Resposta', - 'Send' : 'Enviar', - 'Send a Sample Request' : 'Enviar una petició d\'exemple', - 'show up to version:' : 'mostrar versió:', - 'Size range:' : 'Tamany de rang:', - 'Type' : 'Tipus', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/cs.js b/apidoc/locales/cs.js deleted file mode 100644 index b7796d8f..00000000 --- a/apidoc/locales/cs.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - cs: { - 'Allowed values:' : 'Povolené hodnoty:', - 'Compare all with predecessor': 'Porovnat vše s předchozími verzemi', - 'compare changes to:' : 'porovnat změny s:', - 'compared to' : 'porovnat s', - 'Default value:' : 'Výchozí hodnota:', - 'Description' : 'Popis', - 'Field' : 'Pole', - 'General' : 'Obecné', - 'Generated with' : 'Vygenerováno pomocí', - 'Name' : 'Název', - 'No response values.' : 'Nebyly vráceny žádné hodnoty.', - 'optional' : 'volitelné', - 'Parameter' : 'Parametr', - 'Permission:' : 'Oprávnění:', - 'Response' : 'Odpověď', - 'Send' : 'Odeslat', - 'Send a Sample Request' : 'Odeslat ukázkový požadavek', - 'show up to version:' : 'zobrazit po verzi:', - 'Size range:' : 'Rozsah velikosti:', - 'Type' : 'Typ', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/de.js b/apidoc/locales/de.js deleted file mode 100644 index f66420d0..00000000 --- a/apidoc/locales/de.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - de: { - 'Allowed values:' : 'Erlaubte Werte:', - 'Compare all with predecessor': 'Vergleiche alle mit ihren Vorgängern', - 'compare changes to:' : 'vergleiche Änderungen mit:', - 'compared to' : 'verglichen mit', - 'Default value:' : 'Standardwert:', - 'Description' : 'Beschreibung', - 'Field' : 'Feld', - 'General' : 'Allgemein', - 'Generated with' : 'Erstellt mit', - 'Name' : 'Name', - 'No response values.' : 'Keine Rückgabewerte.', - 'optional' : 'optional', - 'Parameter' : 'Parameter', - 'Permission:' : 'Berechtigung:', - 'Response' : 'Antwort', - 'Send' : 'Senden', - 'Send a Sample Request' : 'Eine Beispielanfrage senden', - 'show up to version:' : 'zeige bis zur Version:', - 'Size range:' : 'Größenbereich:', - 'Type' : 'Typ', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/es.js b/apidoc/locales/es.js deleted file mode 100644 index 3d47e800..00000000 --- a/apidoc/locales/es.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - es: { - 'Allowed values:' : 'Valores permitidos:', - 'Compare all with predecessor': 'Comparar todo con versión anterior', - 'compare changes to:' : 'comparar cambios con:', - 'compared to' : 'comparado con', - 'Default value:' : 'Valor por defecto:', - 'Description' : 'Descripción', - 'Field' : 'Campo', - 'General' : 'General', - 'Generated with' : 'Generado con', - 'Name' : 'Nombre', - 'No response values.' : 'Sin valores en la respuesta.', - 'optional' : 'opcional', - 'Parameter' : 'Parámetro', - 'Permission:' : 'Permisos:', - 'Response' : 'Respuesta', - 'Send' : 'Enviar', - 'Send a Sample Request' : 'Enviar una petición de ejemplo', - 'show up to version:' : 'mostrar a versión:', - 'Size range:' : 'Tamaño de rango:', - 'Type' : 'Tipo', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/fr.js b/apidoc/locales/fr.js deleted file mode 100644 index 100a6429..00000000 --- a/apidoc/locales/fr.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - fr: { - 'Allowed values:' : 'Valeurs autorisées :', - 'Compare all with predecessor': 'Tout comparer avec ...', - 'compare changes to:' : 'comparer les changements à :', - 'compared to' : 'comparer à', - 'Default value:' : 'Valeur par défaut :', - 'Description' : 'Description', - 'Field' : 'Champ', - 'General' : 'Général', - 'Generated with' : 'Généré avec', - 'Name' : 'Nom', - 'No response values.' : 'Aucune valeur de réponse.', - 'optional' : 'optionnel', - 'Parameter' : 'Paramètre', - 'Permission:' : 'Permission :', - 'Response' : 'Réponse', - 'Send' : 'Envoyer', - 'Send a Sample Request' : 'Envoyer une requête représentative', - 'show up to version:' : 'Montrer à partir de la version :', - 'Size range:' : 'Ordre de grandeur :', - 'Type' : 'Type', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/it.js b/apidoc/locales/it.js deleted file mode 100644 index 8117108c..00000000 --- a/apidoc/locales/it.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - it: { - 'Allowed values:' : 'Valori permessi:', - 'Compare all with predecessor': 'Confronta tutto con versioni precedenti', - 'compare changes to:' : 'confronta modifiche con:', - 'compared to' : 'confrontato con', - 'Default value:' : 'Valore predefinito:', - 'Description' : 'Descrizione', - 'Field' : 'Campo', - 'General' : 'Generale', - 'Generated with' : 'Creato con', - 'Name' : 'Nome', - 'No response values.' : 'Nessun valore di risposta.', - 'optional' : 'opzionale', - 'Parameter' : 'Parametro', - 'Permission:' : 'Permessi:', - 'Response' : 'Risposta', - 'Send' : 'Invia', - 'Send a Sample Request' : 'Invia una richiesta di esempio', - 'show up to version:' : 'mostra alla versione:', - 'Size range:' : 'Intervallo dimensione:', - 'Type' : 'Tipo', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/locale.js b/apidoc/locales/locale.js deleted file mode 100644 index 3530705e..00000000 --- a/apidoc/locales/locale.js +++ /dev/null @@ -1,51 +0,0 @@ -define([ - './locales/ca.js', - './locales/cs.js', - './locales/de.js', - './locales/es.js', - './locales/fr.js', - './locales/it.js', - './locales/nl.js', - './locales/pl.js', - './locales/pt_br.js', - './locales/ro.js', - './locales/ru.js', - './locales/tr.js', - './locales/vi.js', - './locales/zh.js', - './locales/zh_cn.js' -], function() { - var langId = (navigator.language || navigator.userLanguage).toLowerCase().replace('-', '_'); - var language = langId.substr(0, 2); - var locales = {}; - - for (index in arguments) { - for (property in arguments[index]) - locales[property] = arguments[index][property]; - } - if ( ! locales['en']) - locales['en'] = {}; - - if ( ! locales[langId] && ! locales[language]) - language = 'en'; - - var locale = (locales[langId] ? locales[langId] : locales[language]); - - function __(text) { - var index = locale[text]; - if (index === undefined) - return text; - return index; - }; - - function setLanguage(language) { - locale = locales[language]; - } - - return { - __ : __, - locales : locales, - locale : locale, - setLanguage: setLanguage - }; -}); diff --git a/apidoc/locales/nl.js b/apidoc/locales/nl.js deleted file mode 100644 index bddfeeb1..00000000 --- a/apidoc/locales/nl.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - nl: { - 'Allowed values:' : 'Toegestane waarden:', - 'Compare all with predecessor': 'Vergelijk alle met voorgaande versie', - 'compare changes to:' : 'vergelijk veranderingen met:', - 'compared to' : 'vergelijk met', - 'Default value:' : 'Standaard waarde:', - 'Description' : 'Omschrijving', - 'Field' : 'Veld', - 'General' : 'Algemeen', - 'Generated with' : 'Gegenereerd met', - 'Name' : 'Naam', - 'No response values.' : 'Geen response waardes.', - 'optional' : 'optioneel', - 'Parameter' : 'Parameter', - 'Permission:' : 'Permissie:', - 'Response' : 'Antwoorden', - 'Send' : 'Sturen', - 'Send a Sample Request' : 'Stuur een sample aanvragen', - 'show up to version:' : 'toon tot en met versie:', - 'Size range:' : 'Maatbereik:', - 'Type' : 'Type', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/pl.js b/apidoc/locales/pl.js deleted file mode 100644 index db645ee1..00000000 --- a/apidoc/locales/pl.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - pl: { - 'Allowed values:' : 'Dozwolone wartości:', - 'Compare all with predecessor': 'Porównaj z poprzednimi wersjami', - 'compare changes to:' : 'porównaj zmiany do:', - 'compared to' : 'porównaj do:', - 'Default value:' : 'Wartość domyślna:', - 'Description' : 'Opis', - 'Field' : 'Pole', - 'General' : 'Generalnie', - 'Generated with' : 'Wygenerowano z', - 'Name' : 'Nazwa', - 'No response values.' : 'Brak odpowiedzi.', - 'optional' : 'opcjonalny', - 'Parameter' : 'Parametr', - 'Permission:' : 'Uprawnienia:', - 'Response' : 'Odpowiedź', - 'Send' : 'Wyślij', - 'Send a Sample Request' : 'Wyślij przykładowe żądanie', - 'show up to version:' : 'pokaż do wersji:', - 'Size range:' : 'Zakres rozmiaru:', - 'Type' : 'Typ', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/pt_br.js b/apidoc/locales/pt_br.js deleted file mode 100644 index 2bd78b0d..00000000 --- a/apidoc/locales/pt_br.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - 'pt_br': { - 'Allowed values:' : 'Valores permitidos:', - 'Compare all with predecessor': 'Compare todos com antecessores', - 'compare changes to:' : 'comparar alterações com:', - 'compared to' : 'comparado com', - 'Default value:' : 'Valor padrão:', - 'Description' : 'Descrição', - 'Field' : 'Campo', - 'General' : 'Geral', - 'Generated with' : 'Gerado com', - 'Name' : 'Nome', - 'No response values.' : 'Sem valores de resposta.', - 'optional' : 'opcional', - 'Parameter' : 'Parâmetro', - 'Permission:' : 'Permissão:', - 'Response' : 'Resposta', - 'Send' : 'Enviar', - 'Send a Sample Request' : 'Enviar um Exemplo de Pedido', - 'show up to version:' : 'aparecer para a versão:', - 'Size range:' : 'Faixa de tamanho:', - 'Type' : 'Tipo', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/ro.js b/apidoc/locales/ro.js deleted file mode 100644 index 8d4e4ed8..00000000 --- a/apidoc/locales/ro.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - ro: { - 'Allowed values:' : 'Valori permise:', - 'Compare all with predecessor': 'Compară toate cu versiunea precedentă', - 'compare changes to:' : 'compară cu versiunea:', - 'compared to' : 'comparat cu', - 'Default value:' : 'Valoare implicită:', - 'Description' : 'Descriere', - 'Field' : 'Câmp', - 'General' : 'General', - 'Generated with' : 'Generat cu', - 'Name' : 'Nume', - 'No response values.' : 'Nici o valoare returnată.', - 'optional' : 'opțional', - 'Parameter' : 'Parametru', - 'Permission:' : 'Permisiune:', - 'Response' : 'Răspuns', - 'Send' : 'Trimite', - 'Send a Sample Request' : 'Trimite o cerere de probă', - 'show up to version:' : 'arată până la versiunea:', - 'Size range:' : 'Interval permis:', - 'Type' : 'Tip', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/ru.js b/apidoc/locales/ru.js deleted file mode 100644 index c5f33821..00000000 --- a/apidoc/locales/ru.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - ru: { - 'Allowed values:' : 'Допустимые значения:', - 'Compare all with predecessor': 'Сравнить с предыдущей версией', - 'compare changes to:' : 'сравнить с:', - 'compared to' : 'в сравнении с', - 'Default value:' : 'По умолчанию:', - 'Description' : 'Описание', - 'Field' : 'Название', - 'General' : 'Общая информация', - 'Generated with' : 'Сгенерировано с помощью', - 'Name' : 'Название', - 'No response values.' : 'Нет значений для ответа.', - 'optional' : 'необязательный', - 'Parameter' : 'Параметр', - 'Permission:' : 'Разрешено:', - 'Response' : 'Ответ', - 'Send' : 'Отправить', - 'Send a Sample Request' : 'Отправить тестовый запрос', - 'show up to version:' : 'показать версию:', - 'Size range:' : 'Ограничения:', - 'Type' : 'Тип', - 'url' : 'URL' - } -}); diff --git a/apidoc/locales/tr.js b/apidoc/locales/tr.js deleted file mode 100644 index 5c64e52d..00000000 --- a/apidoc/locales/tr.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - tr: { - 'Allowed values:' : 'İzin verilen değerler:', - 'Compare all with predecessor': 'Tümünü öncekiler ile karşılaştır', - 'compare changes to:' : 'değişiklikleri karşılaştır:', - 'compared to' : 'karşılaştır', - 'Default value:' : 'Varsayılan değer:', - 'Description' : 'Açıklama', - 'Field' : 'Alan', - 'General' : 'Genel', - 'Generated with' : 'Oluşturan', - 'Name' : 'İsim', - 'No response values.' : 'Dönüş verisi yok.', - 'optional' : 'opsiyonel', - 'Parameter' : 'Parametre', - 'Permission:' : 'İzin:', - 'Response' : 'Dönüş', - 'Send' : 'Gönder', - 'Send a Sample Request' : 'Örnek istek gönder', - 'show up to version:' : 'bu versiyona kadar göster:', - 'Size range:' : 'Boyut aralığı:', - 'Type' : 'Tip', - 'url' : 'url' - } -}); diff --git a/apidoc/locales/vi.js b/apidoc/locales/vi.js deleted file mode 100644 index 7ce77050..00000000 --- a/apidoc/locales/vi.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - vi: { - 'Allowed values:' : 'Giá trị chấp nhận:', - 'Compare all with predecessor': 'So sánh với tất cả phiên bản trước', - 'compare changes to:' : 'so sánh sự thay đổi với:', - 'compared to' : 'so sánh với', - 'Default value:' : 'Giá trị mặc định:', - 'Description' : 'Chú thích', - 'Field' : 'Trường dữ liệu', - 'General' : 'Tổng quan', - 'Generated with' : 'Được tạo bởi', - 'Name' : 'Tên', - 'No response values.' : 'Không có kết quả trả về.', - 'optional' : 'Tùy chọn', - 'Parameter' : 'Tham số', - 'Permission:' : 'Quyền hạn:', - 'Response' : 'Kết quả', - 'Send' : 'Gửi', - 'Send a Sample Request' : 'Gửi một yêu cầu mẫu', - 'show up to version:' : 'hiển thị phiên bản:', - 'Size range:' : 'Kích cỡ:', - 'Type' : 'Kiểu', - 'url' : 'liên kết' - } -}); diff --git a/apidoc/locales/zh.js b/apidoc/locales/zh.js deleted file mode 100644 index ca5042fe..00000000 --- a/apidoc/locales/zh.js +++ /dev/null @@ -1,25 +0,0 @@ -define({ - zh: { - 'Allowed values​​:' : '允許值:', - 'Compare all with predecessor': '預先比較所有', - 'compare changes to:' : '比較變更:', - 'compared to' : '對比', - 'Default value:' : '預設值:', - 'Description' : '描述', - 'Field' : '欄位', - 'General' : '概括', - 'Generated with' : '生成工具', - 'Name' : '名稱', - 'No response values​​.' : '無對應資料.', - 'optional' : '選填', - 'Parameter' : '參數', - 'Permission:' : '權限:', - 'Response' : '回應', - 'Send' : '發送', - 'Send a Sample Request' : '發送試用需求', - 'show up to version:' : '顯示到版本:', - 'Size range:' : '區間:', - 'Type' : '類型', - 'url' : '網址' - } -}); diff --git a/apidoc/locales/zh_cn.js b/apidoc/locales/zh_cn.js deleted file mode 100644 index 50913e25..00000000 --- a/apidoc/locales/zh_cn.js +++ /dev/null @@ -1,27 +0,0 @@ -define({ - 'zh_cn': { - 'Allowed values:' : '允许值:', - 'Compare all with predecessor': '与所有较早的比较', - 'compare changes to:' : '将当前版本与指定版本比较:', - 'compared to' : '相比于', - 'Default value:' : '默认值:', - 'Description' : '描述', - 'Field' : '字段', - 'General' : '概要', - 'Generated with' : '基于', - 'Name' : '名称', - 'No response values.' : '无返回值.', - 'optional' : '可选', - 'Parameter' : '参数', - 'Parameters' : '参数', - 'Headers' : '头部参数', - 'Permission:' : '权限:', - 'Response' : '返回', - 'Send' : '发送', - 'Send a Sample Request' : '发送示例请求', - 'show up to version:' : '显示到指定版本:', - 'Size range:' : '取值范围:', - 'Type' : '类型', - 'url' : '网址' - } -}); diff --git a/apidoc/main.js b/apidoc/main.js deleted file mode 100644 index bdcf58cb..00000000 --- a/apidoc/main.js +++ /dev/null @@ -1,1146 +0,0 @@ -require.config({ - paths: { - bootstrap: "./vendor/bootstrap.min", - diffMatchPatch: "./vendor/diff_match_patch.min", - handlebars: "./vendor/handlebars.min", - handlebarsExtended: "./utils/handlebars_helper", - jquery: "./vendor/jquery.min", - locales: "./locales/locale", - lodash: "./vendor/lodash.custom.min", - pathToRegexp: "./vendor/path-to-regexp/index", - prismjs: "./vendor/prism", - semver: "./vendor/semver.min", - utilsSampleRequest: "./utils/send_sample_request", - webfontloader: "./vendor/webfontloader", - list: "./vendor/list.min", - apiData: "./api_data", - apiProject: "./api_project" - }, - shim: { - bootstrap: { - deps: ["jquery"] - }, - diffMatchPatch: { - exports: "diff_match_patch" - }, - handlebars: { - exports: "Handlebars" - }, - handlebarsExtended: { - deps: ["jquery", "handlebars"], - exports: "Handlebars" - }, - prismjs: { - exports: "Prism" - } - }, - urlArgs: "v=" + new Date().getTime(), - waitSeconds: 150 -}); - -require([ - "jquery", - "lodash", - "locales", - "handlebarsExtended", - "apiProject", - "apiData", - "prismjs", - "utilsSampleRequest", - "semver", - "webfontloader", - "bootstrap", - "pathToRegexp", - "list" -], function( - $, - _, - locale, - Handlebars, - apiProject, - apiData, - Prism, - sampleRequest, - semver, - WebFont -) { - // Load google web fonts. - WebFont.load({ - active: function() { - // Only init after fonts are loaded. - init( - $, - _, - locale, - Handlebars, - apiProject, - apiData, - Prism, - sampleRequest, - semver - ); - }, - inactive: function() { - // Run init, even if loading fonts fails - init( - $, - _, - locale, - Handlebars, - apiProject, - apiData, - Prism, - sampleRequest, - semver - ); - }, - google: { - families: ["Source Code Pro", "Source Sans Pro:n4,n6,n7"] - } - }); -}); - -function init( - $, - _, - locale, - Handlebars, - apiProject, - apiData, - Prism, - sampleRequest, - semver -) { - var api = apiData.api; - - // - // Templates - // - var templateHeader = Handlebars.compile($("#template-header").html()); - var templateFooter = Handlebars.compile($("#template-footer").html()); - var templateArticle = Handlebars.compile($("#template-article").html()); - var templateCompareArticle = Handlebars.compile( - $("#template-compare-article").html() - ); - var templateGenerator = Handlebars.compile($("#template-generator").html()); - var templateProject = Handlebars.compile($("#template-project").html()); - var templateSections = Handlebars.compile($("#template-sections").html()); - var templateSidenav = Handlebars.compile($("#template-sidenav").html()); - - // - // Default host url used if no sampleUrl is present in config - // - var baseURL = window.location.origin; - - // - // apiProject defaults - // - if (!apiProject.template) apiProject.template = {}; - - if (apiProject.template.withCompare == null) - apiProject.template.withCompare = true; - - if (apiProject.template.withGenerator == null) - apiProject.template.withGenerator = true; - - if (apiProject.template.forceLanguage) - locale.setLanguage(apiProject.template.forceLanguage); - - if (apiProject.template.aloneDisplay == null) - apiProject.template.aloneDisplay = false; - - // Setup jQuery Ajax - $.ajaxSetup(apiProject.template.jQueryAjaxSetup); - - // - // Data transform - // - // grouped by group - var apiByGroup = _.groupBy(api, function(entry) { - return entry.group; - }); - - // grouped by group and name - var apiByGroupAndName = {}; - $.each(apiByGroup, function(index, entries) { - apiByGroupAndName[index] = _.groupBy(entries, function(entry) { - return entry.name; - }); - }); - - // - // sort api within a group by title ASC and custom order - // - var newList = []; - var umlauts = { ä: "ae", ü: "ue", ö: "oe", ß: "ss" }; // TODO: remove in version 1.0 - $.each(apiByGroupAndName, function(index, groupEntries) { - // get titles from the first entry of group[].name[] (name has versioning) - var titles = []; - $.each(groupEntries, function(titleName, entries) { - var title = entries[0].title; - if (title !== undefined) { - title.toLowerCase().replace(/[äöüß]/g, function($0) { - return umlauts[$0]; - }); - titles.push(title + "#~#" + titleName); // '#~#' keep reference to titleName after sorting - } - }); - // sort by name ASC - titles.sort(); - - // custom order - if (apiProject.order) titles = sortByOrder(titles, apiProject.order, "#~#"); - - // add single elements to the new list - titles.forEach(function(name) { - var values = name.split("#~#"); - var key = values[1]; - groupEntries[key].forEach(function(entry) { - newList.push(entry); - }); - }); - }); - // api overwrite with ordered list - api = newList; - - // - // Group- and Versionlists - // - var apiGroups = {}; - var apiGroupTitles = {}; - var apiVersions = {}; - apiVersions[apiProject.version] = 1; - - $.each(api, function(index, entry) { - apiGroups[entry.group] = 1; - apiGroupTitles[entry.group] = entry.groupTitle || entry.group; - apiVersions[entry.version] = 1; - }); - - // sort groups - apiGroups = Object.keys(apiGroups); - apiGroups.sort(); - - // custom order - if (apiProject.order) apiGroups = sortByOrder(apiGroups, apiProject.order); - - // sort versions DESC - apiVersions = Object.keys(apiVersions); - apiVersions.sort(semver.compare); - apiVersions.reverse(); - - // - // create Navigationlist - // - var nav = []; - apiGroups.forEach(function(group) { - // Mainmenu entry - nav.push({ - group: group, - isHeader: true, - title: apiGroupTitles[group] - }); - - // Submenu - var oldName = ""; - api.forEach(function(entry) { - if (entry.group === group) { - if (oldName !== entry.name) { - nav.push({ - title: entry.title, - group: group, - name: entry.name, - type: entry.type, - version: entry.version, - url: entry.url - }); - } else { - nav.push({ - title: entry.title, - group: group, - hidden: true, - name: entry.name, - type: entry.type, - version: entry.version, - url: entry.url - }); - } - oldName = entry.name; - } - }); - }); - - /** - * Add navigation items by analyzing the HTML content and searching for h1 and h2 tags - * @param nav Object the navigation array - * @param content string the compiled HTML content - * @param index where to insert items - * @return boolean true if any good-looking (i.e. with a group identifier)

tag was found - */ - function add_nav(nav, content, index) { - var found_level1 = false; - if (!content) { - return found_level1; - } - var topics = content.match(/(.+?)<\/h(1|2)>/gi); - if (topics) { - topics.forEach(function(entry) { - var level = entry.substring(2, 3); - var title = entry.replace(/<.+?>/g, ""); // Remove all HTML tags for the title - var entry_tags = entry.match(/id="api-([^\-]+)(?:-(.+))?"/); // Find the group and name in the id property - var group = entry_tags ? entry_tags[1] : null; - var name = entry_tags ? entry_tags[2] : null; - if (level == 1 && title && group) { - nav.splice(index, 0, { - group: group, - isHeader: true, - title: title, - isFixed: true - }); - index++; - found_level1 = true; - } - if (level == 2 && title && group && name) { - nav.splice(index, 0, { - group: group, - name: name, - isHeader: false, - title: title, - isFixed: false, - version: "1.0" - }); - index++; - } - }); - } - return found_level1; - } - - // Mainmenu Header entry - if (apiProject.header) { - var found_level1 = add_nav(nav, apiProject.header.content, 0); // Add level 1 and 2 titles - if (!found_level1) { - // If no Level 1 tags were found, make a title - nav.unshift({ - group: "_", - isHeader: true, - title: - apiProject.header.title == null - ? locale.__("General") - : apiProject.header.title, - isFixed: true - }); - } - } - - // Mainmenu Footer entry - if (apiProject.footer) { - var last_nav_index = nav.length; - var found_level1 = add_nav(nav, apiProject.footer.content, nav.length); // Add level 1 and 2 titles - if (!found_level1 && apiProject.footer.title != null) { - // If no Level 1 tags were found, make a title - nav.splice(last_nav_index, 0, { - group: "_footer", - isHeader: true, - title: apiProject.footer.title, - isFixed: true - }); - } - } - - // render pagetitle - var title = apiProject.title - ? apiProject.title - : "apiDoc: " + apiProject.name + " - " + apiProject.version; - $(document).attr("title", title); - - // remove loader - $("#loader").remove(); - - // render sidenav - var fields = { - nav: nav - }; - $("#sidenav").append(templateSidenav(fields)); - - // render Generator - $("#generator").append(templateGenerator(apiProject)); - - // render Project - _.extend(apiProject, { versions: apiVersions }); - $("#project").append(templateProject(apiProject)); - - // render apiDoc, header/footer documentation - if (apiProject.header) $("#header").append(templateHeader(apiProject.header)); - - if (apiProject.footer) $("#footer").append(templateFooter(apiProject.footer)); - - // - // Render Sections and Articles - // - var articleVersions = {}; - var content = ""; - apiGroups.forEach(function(groupEntry) { - var articles = []; - var oldName = ""; - var fields = {}; - var title = groupEntry; - var description = ""; - articleVersions[groupEntry] = {}; - - // render all articles of a group - api.forEach(function(entry) { - if (groupEntry === entry.group) { - if (oldName !== entry.name) { - // determine versions - api.forEach(function(versionEntry) { - if ( - groupEntry === versionEntry.group && - entry.name === versionEntry.name - ) { - if (!articleVersions[entry.group].hasOwnProperty(entry.name)) { - articleVersions[entry.group][entry.name] = []; - } - articleVersions[entry.group][entry.name].push( - versionEntry.version - ); - } - }); - fields = { - article: entry, - versions: articleVersions[entry.group][entry.name] - }; - } else { - fields = { - article: entry, - hidden: true, - versions: articleVersions[entry.group][entry.name] - }; - } - - if (apiProject.sampleUrl == false) { - fields.article.sampleRequest = [ - { - url: baseURL + fields.article.url - } - ]; - } - - // add prefix URL for endpoint unless it's already absolute - if (apiProject.url) { - if (fields.article.url.substr(0, 4).toLowerCase() !== "http") { - fields.article.url = apiProject.url + fields.article.url; - } - } - - addArticleSettings(fields, entry); - - if (entry.groupTitle) title = entry.groupTitle; - - // TODO: make groupDescription compareable with older versions (not important for the moment) - if (entry.groupDescription) description = entry.groupDescription; - - articles.push({ - article: templateArticle(fields), - group: entry.group, - name: entry.name, - aloneDisplay: apiProject.template.aloneDisplay - }); - oldName = entry.name; - } - }); - - // render Section with Articles - var fields = { - group: groupEntry, - title: title, - description: description, - articles: articles, - aloneDisplay: apiProject.template.aloneDisplay - }; - content += templateSections(fields); - }); - $("#sections").append(content); - - // Bootstrap Scrollspy - $(this).scrollspy({ target: "#scrollingNav" }); - - // Content-Scroll on Navigation click. - $(".sidenav") - .find("a") - .on("click", function(e) { - e.preventDefault(); - var id = $(this).attr("href"); - if ($(id).length > 0) - $("html,body").animate( - { scrollTop: parseInt($(id).offset().top) }, - 400 - ); - window.location.hash = $(this).attr("href"); - }); - - /** - * Check if Parameter (sub) List has a type Field. - * Example: @apiSuccess varname1 No type. - * @apiSuccess {String} varname2 With type. - * - * @param {Object} fields - */ - function _hasTypeInFields(fields) { - var result = false; - $.each(fields, function(name) { - result = - result || - _.some(fields[name], function(item) { - return item.type; - }); - }); - return result; - } - - /** - * On Template changes, recall plugins. - */ - function initDynamic() { - // Bootstrap popover - $('button[data-toggle="popover"]') - .popover() - .click(function(e) { - e.preventDefault(); - }); - - var version = $("#version strong").html(); - $("#sidenav li").removeClass("is-new"); - if (apiProject.template.withCompare) { - $("#sidenav li[data-version='" + version + "']").each(function() { - var group = $(this).data("group"); - var name = $(this).data("name"); - var length = $( - "#sidenav li[data-group='" + group + "'][data-name='" + name + "']" - ).length; - var index = $( - "#sidenav li[data-group='" + group + "'][data-name='" + name + "']" - ).index($(this)); - if (length === 1 || index === length - 1) $(this).addClass("is-new"); - }); - } - - // tabs - $(".nav-tabs-examples a").click(function(e) { - e.preventDefault(); - $(this).tab("show"); - }); - $(".nav-tabs-examples") - .find("a:first") - .tab("show"); - - // sample header-content-type switch - $(".sample-header-content-type-switch").change(function() { - var paramName = "." + $(this).attr("name") + "-fields"; - var bodyName = "." + $(this).attr("name") + "-body"; - var selectName = "select[name=" + $(this).attr("name") + "]"; - if ($(this).val() == "body-json") { - $(selectName).val("undefined"); - $(this).val("body-json"); - $(paramName).removeClass("hide"); - $(this) - .parent() - .nextAll(paramName) - .first() - .addClass("hide"); - $(bodyName).addClass("hide"); - $(this) - .parent() - .nextAll(bodyName) - .first() - .removeClass("hide"); - } else if ($(this).val() == "body-form-data") { - $(selectName).val("undefined"); - $(this).val("body-form-data"); - $(bodyName).addClass("hide"); - $(paramName).removeClass("hide"); - } else { - $(this) - .parent() - .nextAll(paramName) - .first() - .removeClass("hide"); - $(this) - .parent() - .nextAll(bodyName) - .first() - .addClass("hide"); - } - $(this) - .prev(".sample-request-switch") - .prop("checked", true); - }); - - // sample request switch - $(".sample-request-switch").click(function(e) { - var paramName = "." + $(this).attr("name") + "-fields"; - var bodyName = "." + $(this).attr("name") + "-body"; - var select = $(this) - .next("." + $(this).attr("name") + "-select") - .val(); - if ($(this).prop("checked")) { - if (select == "body-json") { - $(this) - .parent() - .nextAll(bodyName) - .first() - .removeClass("hide"); - } else { - $(this) - .parent() - .nextAll(paramName) - .first() - .removeClass("hide"); - } - } else { - if (select == "body-json") { - $(this) - .parent() - .nextAll(bodyName) - .first() - .addClass("hide"); - } else { - $(this) - .parent() - .nextAll(paramName) - .first() - .addClass("hide"); - } - } - }); - - if (apiProject.template.aloneDisplay) { - //show group - $(".show-group").click(function() { - var apiGroup = "." + $(this).attr("data-group") + "-group"; - var apiGroupArticle = "." + $(this).attr("data-group") + "-article"; - $(".show-api-group").addClass("hide"); - $(apiGroup).removeClass("hide"); - $(".show-api-article").addClass("hide"); - $(apiGroupArticle).removeClass("hide"); - }); - - //show api - $(".show-api").click(function() { - var apiName = "." + $(this).attr("data-name") + "-article"; - var apiGroup = "." + $(this).attr("data-group") + "-group"; - $(".show-api-group").addClass("hide"); - $(apiGroup).removeClass("hide"); - $(".show-api-article").addClass("hide"); - $(apiName).removeClass("hide"); - }); - } - - // call scrollspy refresh method - $(window).scrollspy("refresh"); - - // init modules - sampleRequest.initDynamic(); - Prism.highlightAll(); - } - initDynamic(); - - if (apiProject.template.aloneDisplay) { - var hashVal = window.location.hash; - if (hashVal != null && hashVal.length !== 0) { - $("." + hashVal.slice(1) + "-init").click(); - } - } - - // - // HTML-Template specific jQuery-Functions - // - // Change Main Version - function setMainVersion(selectedVersion) { - if (typeof selectedVersion === "undefined") { - selectedVersion = $("#version strong").html(); - } else { - $("#version strong").html(selectedVersion); - } - - // hide all - $("article").addClass("hide"); - $("#sidenav li:not(.nav-fixed)").addClass("hide"); - - // show 1st equal or lower Version of each entry - $("article[data-version]").each(function(index) { - var group = $(this).data("group"); - var name = $(this).data("name"); - var version = $(this).data("version"); - - if (semver.lte(version, selectedVersion)) { - if ( - $( - "article[data-group='" + - group + - "'][data-name='" + - name + - "']:visible" - ).length === 0 - ) { - // enable Article - $( - "article[data-group='" + - group + - "'][data-name='" + - name + - "'][data-version='" + - version + - "']" - ).removeClass("hide"); - // enable Navigation - $( - "#sidenav li[data-group='" + - group + - "'][data-name='" + - name + - "'][data-version='" + - version + - "']" - ).removeClass("hide"); - $("#sidenav li.nav-header[data-group='" + group + "']").removeClass( - "hide" - ); - } - } - }); - - // show 1st equal or lower Version of each entry - $("article[data-version]").each(function(index) { - var group = $(this).data("group"); - $("section#api-" + group).removeClass("hide"); - if ($("section#api-" + group + " article:visible").length === 0) { - $("section#api-" + group).addClass("hide"); - } else { - $("section#api-" + group).removeClass("hide"); - } - }); - - initDynamic(); - return; - } - setMainVersion(); - - $("#versions li.version a").on("click", function(e) { - e.preventDefault(); - - setMainVersion($(this).html()); - }); - - // compare all article with their predecessor - $("#compareAllWithPredecessor").on("click", changeAllVersionCompareTo); - - // change version of an article - $("article .versions li.version a").on("click", changeVersionCompareTo); - - // compare url-parameter - $.urlParam = function(name) { - var results = new RegExp("[\\?&]" + name + "=([^&#]*)").exec( - window.location.href - ); - return results && results[1] ? results[1] : null; - }; - - if ($.urlParam("compare")) { - // URL Paramter ?compare=1 is set - $("#compareAllWithPredecessor").trigger("click"); - } - - // Quick jump on page load to hash position. - // Should happen after setting the main version - // and after triggering the click on the compare button, - // as these actions modify the content - // and would make it jump to the wrong position or not jump at all. - if (window.location.hash) { - var id = decodeURI(window.location.hash); - if ($(id).length > 0) - $("html,body").animate({ scrollTop: parseInt($(id).offset().top) }, 0); - } - - /** - * Initialize search - */ - var options = { - valueNames: ["nav-list-item", "nav-list-url-item"] - }; - var endpointsList = new List("scrollingNav", options); - - /** - * Set initial focus to search input - */ - $("#scrollingNav .sidenav-search input.search").focus(); - - /** - * Detect ESC key to reset search - */ - $(document).keyup(function(e) { - if (e.keyCode === 27) $("span.search-reset").click(); - }); - - /** - * Search reset - */ - $("span.search-reset").on("click", function() { - $("#scrollingNav .sidenav-search input.search") - .val("") - .focus(); - endpointsList.search(); - }); - - /** - * Change version of an article to compare it to an other version. - */ - function changeVersionCompareTo(e) { - e.preventDefault(); - - var $root = $(this).parents("article"); - var selectedVersion = $(this).html(); - var $button = $root.find(".version"); - var currentVersion = $button.find("strong").html(); - $button.find("strong").html(selectedVersion); - - var group = $root.data("group"); - var name = $root.data("name"); - var version = $root.data("version"); - - var compareVersion = $root.data("compare-version"); - - if (compareVersion === selectedVersion) return; - - if (!compareVersion && version == selectedVersion) return; - - if ( - (compareVersion && articleVersions[group][name][0] === selectedVersion) || - version === selectedVersion - ) { - // the version of the entry is set to the highest version (reset) - resetArticle(group, name, version); - } else { - var $compareToArticle = $( - "article[data-group='" + - group + - "'][data-name='" + - name + - "'][data-version='" + - selectedVersion + - "']" - ); - - var sourceEntry = {}; - var compareEntry = {}; - $.each(apiByGroupAndName[group][name], function(index, entry) { - if (entry.version === version) sourceEntry = entry; - if (entry.version === selectedVersion) compareEntry = entry; - }); - - var fields = { - article: sourceEntry, - compare: compareEntry, - versions: articleVersions[group][name] - }; - - // add unique id - // TODO: replace all group-name-version in template with id. - fields.article.id = - fields.article.group + - "-" + - fields.article.name + - "-" + - fields.article.version; - fields.article.id = fields.article.id.replace(/\./g, "_"); - - fields.compare.id = - fields.compare.group + - "-" + - fields.compare.name + - "-" + - fields.compare.version; - fields.compare.id = fields.compare.id.replace(/\./g, "_"); - - var entry = sourceEntry; - if (entry.parameter && entry.parameter.fields) - fields._hasTypeInParameterFields = _hasTypeInFields( - entry.parameter.fields - ); - - if (entry.error && entry.error.fields) - fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields); - - if (entry.success && entry.success.fields) - fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields); - - if (entry.info && entry.info.fields) - fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields); - - var entry = compareEntry; - if ( - fields._hasTypeInParameterFields !== true && - entry.parameter && - entry.parameter.fields - ) - fields._hasTypeInParameterFields = _hasTypeInFields( - entry.parameter.fields - ); - - if ( - fields._hasTypeInErrorFields !== true && - entry.error && - entry.error.fields - ) - fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields); - - if ( - fields._hasTypeInSuccessFields !== true && - entry.success && - entry.success.fields - ) - fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields); - - if ( - fields._hasTypeInInfoFields !== true && - entry.info && - entry.info.fields - ) - fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields); - - var content = templateCompareArticle(fields); - $root.after(content); - var $content = $root.next(); - - // Event on.click re-assign - $content - .find(".versions li.version a") - .on("click", changeVersionCompareTo); - - // select navigation - $( - "#sidenav li[data-group='" + - group + - "'][data-name='" + - name + - "'][data-version='" + - currentVersion + - "']" - ).addClass("has-modifications"); - - $root.remove(); - // TODO: on change main version or select the highest version re-render - } - - initDynamic(); - } - - /** - * Compare all currently selected Versions with their predecessor. - */ - function changeAllVersionCompareTo(e) { - e.preventDefault(); - $("article:visible .versions").each(function() { - var $root = $(this).parents("article"); - var currentVersion = $root.data("version"); - var $foundElement = null; - $(this) - .find("li.version a") - .each(function() { - var selectVersion = $(this).html(); - if (selectVersion < currentVersion && !$foundElement) - $foundElement = $(this); - }); - - if ($foundElement) $foundElement.trigger("click"); - }); - initDynamic(); - } - - /** - * Sort the fields. - */ - function sortFields(fields_object) { - $.each(fields_object, function(key, fields) { - // Find only object fields - var objects = fields.filter(function(item) { - return item.type === "Object"; - }); - - // Check if has any object - if (objects.length === 0) { - return; - } - - // Iterate over all objects - for (var object of objects) { - // Retrieve the index - var index = fields.indexOf(object); - - // Find all child fields for this object - var objectFields = fields.filter(function(item) { - return item.field.indexOf(object.field + ".") > -1; - }); - - // Get the child index - var firstIndex = fields.indexOf(objectFields[0]); - - // Put the object it before the first child index - fields.splice(index, 1); - fields.splice(firstIndex, 0, object); - - // Startup the last index with the object index - var lastIndex = firstIndex; - - // Iterate over all children - for (var child of objectFields) { - lastIndex++; - - // Retrieve the index - var childIndex = fields.indexOf(child); - - // Put it after the object declaration - fields.splice(childIndex, 1); - fields.splice(lastIndex, 0, child); - } - } - - // Retrieve the first object field index - var firstObjectIndex = fields.indexOf(objects[0]); - - // Find all non-object fields that doesn't contain dot notation - var nonObjects = fields.filter(function(item) { - return item.field.indexOf(".") === -1 && item.type !== "Object"; - }); - - // Iterate over all non-objects - for (var nonObject of nonObjects) { - // Put it before the first object field - var index = fields.indexOf(nonObject); - fields.splice(index, 1); - fields.splice(firstObjectIndex, 0, nonObject); - } - }); - } - - /** - * Add article settings. - */ - function addArticleSettings(fields, entry) { - // add unique id - // TODO: replace all group-name-version in template with id. - fields.id = - fields.article.group + - "-" + - fields.article.name + - "-" + - fields.article.version; - fields.id = fields.id.replace(/\./g, "_"); - - if (entry.header && entry.header.fields) { - sortFields(entry.header.fields); - fields._hasTypeInHeaderFields = _hasTypeInFields(entry.header.fields); - } - - if (entry.parameter && entry.parameter.fields) { - sortFields(entry.parameter.fields); - fields._hasTypeInParameterFields = _hasTypeInFields( - entry.parameter.fields - ); - } - - if (entry.error && entry.error.fields) { - sortFields(entry.error.fields); - fields._hasTypeInErrorFields = _hasTypeInFields(entry.error.fields); - } - - if (entry.success && entry.success.fields) { - sortFields(entry.success.fields); - fields._hasTypeInSuccessFields = _hasTypeInFields(entry.success.fields); - } - - if (entry.info && entry.info.fields) { - sortFields(entry.info.fields); - fields._hasTypeInInfoFields = _hasTypeInFields(entry.info.fields); - } - - // add template settings - fields.template = apiProject.template; - } - - /** - * Render Article. - */ - function renderArticle(group, name, version) { - var entry = {}; - $.each(apiByGroupAndName[group][name], function(index, currentEntry) { - if (currentEntry.version === version) entry = currentEntry; - }); - var fields = { - article: entry, - versions: articleVersions[group][name] - }; - - addArticleSettings(fields, entry); - - return templateArticle(fields); - } - - /** - * Render original Article and remove the current visible Article. - */ - function resetArticle(group, name, version) { - var $root = $( - "article[data-group='" + group + "'][data-name='" + name + "']:visible" - ); - var content = renderArticle(group, name, version); - - $root.after(content); - var $content = $root.next(); - - // Event on.click needs to be reassigned (should actually work with on ... automatically) - $content.find(".versions li.version a").on("click", changeVersionCompareTo); - - $( - "#sidenav li[data-group='" + - group + - "'][data-name='" + - name + - "'][data-version='" + - version + - "']" - ).removeClass("has-modifications"); - - $root.remove(); - return; - } - - /** - * Return ordered entries by custom order and append not defined entries to the end. - * @param {String[]} elements - * @param {String[]} order - * @param {String} splitBy - * @return {String[]} Custom ordered list. - */ - function sortByOrder(elements, order, splitBy) { - var results = []; - order.forEach(function(name) { - if (splitBy) - elements.forEach(function(element) { - var parts = element.split(splitBy); - var key = parts[0]; // reference keep for sorting - if (key == name || parts[1] == name) results.push(element); - }); - else - elements.forEach(function(key) { - if (key == name) results.push(name); - }); - }); - // Append all other entries that ar not defined in order - elements.forEach(function(element) { - if (results.indexOf(element) === -1) results.push(element); - }); - return results; - } - Prism.highlightAll(); -} diff --git a/apidoc/utils/handlebars_helper.js b/apidoc/utils/handlebars_helper.js deleted file mode 100644 index ed1dc1da..00000000 --- a/apidoc/utils/handlebars_helper.js +++ /dev/null @@ -1,416 +0,0 @@ -define([ - 'locales', - 'handlebars', - 'diffMatchPatch' -], function(locale, Handlebars, DiffMatchPatch) { - - /** - * Return a text as markdown. - * Currently only a little helper to replace apidoc-inline Links (#Group:Name). - * Should be replaced with a full markdown lib. - * @param string text - */ - Handlebars.registerHelper('markdown', function(text) { - if ( ! text ) { - return text; - } - text = text.replace(/((\[(.*?)\])?\(#)((.+?):(.+?))(\))/mg, function(match, p1, p2, p3, p4, p5, p6) { - var link = p3 || p5 + '/' + p6; - return '' + link + ''; - }); - return text; - }); - - /** - * set paramater type. - */ - Handlebars.registerHelper("setInputType", function(text) { - if (text === "File") { - return "file"; - } - return "text"; - }); - - /** - * start/stop timer for simple performance check. - */ - var timer; - Handlebars.registerHelper('startTimer', function(text) { - timer = new Date(); - return ''; - }); - - Handlebars.registerHelper('stopTimer', function(text) { - console.log(new Date() - timer); - return ''; - }); - - /** - * Return localized Text. - * @param string text - */ - Handlebars.registerHelper('__', function(text) { - return locale.__(text); - }); - - /** - * Console log. - * @param mixed obj - */ - Handlebars.registerHelper('cl', function(obj) { - console.log(obj); - return ''; - }); - - /** - * Replace underscore with space. - * @param string text - */ - Handlebars.registerHelper('underscoreToSpace', function(text) { - return text.replace(/(_+)/g, ' '); - }); - - /** - * - */ - Handlebars.registerHelper('assign', function(name) { - if(arguments.length > 0) { - var type = typeof(arguments[1]); - var arg = null; - if(type === 'string' || type === 'number' || type === 'boolean') arg = arguments[1]; - Handlebars.registerHelper(name, function() { return arg; }); - } - return ''; - }); - - /** - * - */ - Handlebars.registerHelper('nl2br', function(text) { - return _handlebarsNewlineToBreak(text); - }); - - /** - * - */ - Handlebars.registerHelper('if_eq', function(context, options) { - var compare = context; - // Get length if context is an object - if (context instanceof Object && ! (options.hash.compare instanceof Object)) - compare = Object.keys(context).length; - - if (compare === options.hash.compare) - return options.fn(this); - - return options.inverse(this); - }); - - /** - * - */ - Handlebars.registerHelper('if_gt', function(context, options) { - var compare = context; - // Get length if context is an object - if (context instanceof Object && ! (options.hash.compare instanceof Object)) - compare = Object.keys(context).length; - - if(compare > options.hash.compare) - return options.fn(this); - - return options.inverse(this); - }); - - /** - * - */ - var templateCache = {}; - Handlebars.registerHelper('subTemplate', function(name, sourceContext) { - if ( ! templateCache[name]) - templateCache[name] = Handlebars.compile($('#template-' + name).html()); - - var template = templateCache[name]; - var templateContext = $.extend({}, this, sourceContext.hash); - return new Handlebars.SafeString( template(templateContext) ); - }); - - /** - * - */ - Handlebars.registerHelper('toLowerCase', function(value) { - return (value && typeof value === 'string') ? value.toLowerCase() : ''; - }); - - /** - * - */ - Handlebars.registerHelper('splitFill', function(value, splitChar, fillChar) { - var splits = value.split(splitChar); - return new Array(splits.length).join(fillChar) + splits[splits.length - 1]; - }); - - /** - * Convert Newline to HTML-Break (nl2br). - * - * @param {String} text - * @returns {String} - */ - function _handlebarsNewlineToBreak(text) { - return ('' + text).replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '
' + '$2'); - } - - /** - * - */ - Handlebars.registerHelper('each_compare_list_field', function(source, compare, options) { - var fieldName = options.hash.field; - var newSource = []; - if (source) { - source.forEach(function(entry) { - var values = entry; - values['key'] = entry[fieldName]; - newSource.push(values); - }); - } - - var newCompare = []; - if (compare) { - compare.forEach(function(entry) { - var values = entry; - values['key'] = entry[fieldName]; - newCompare.push(values); - }); - } - return _handlebarsEachCompared('key', newSource, newCompare, options); - }); - - /** - * - */ - Handlebars.registerHelper('each_compare_keys', function(source, compare, options) { - var newSource = []; - if (source) { - var sourceFields = Object.keys(source); - sourceFields.forEach(function(name) { - var values = {}; - values['value'] = source[name]; - values['key'] = name; - newSource.push(values); - }); - } - - var newCompare = []; - if (compare) { - var compareFields = Object.keys(compare); - compareFields.forEach(function(name) { - var values = {}; - values['value'] = compare[name]; - values['key'] = name; - newCompare.push(values); - }); - } - return _handlebarsEachCompared('key', newSource, newCompare, options); - }); - - /** - * - */ - Handlebars.registerHelper('gen_body', function(context, options) { - let strBody = {}; - context.forEach(element => { - element.field = element.field.replace("]", ""); - switch (element.type.toLowerCase()) { - case "string": - if (element.field.includes('[')) { - if (strBody[element.field.split("[")[0]] === undefined) { - strBody[element.field.split("[")[0]] = {}; - } - strBody[element.field.split("[")[0]][element.field.split("[")[1]] = (element.defaultValue || ""); - break; - } - strBody[element.field] = (element.defaultValue || ""); - break; - case "number": - if (element.field.includes('[')) { - if (strBody[element.field.split("[")[0]] === undefined) { - strBody[element.field.split("[")[0]] = {}; - } - strBody[element.field.split("[")[0]][element.field.split("[")[1]] = (element.defaultValue || 0); - break; - } - strBody[element.field] = (element.defaultValue || 0); - break; - case "object": - if (strBody[element.field] === undefined) { - strBody[element.field] = {}; - } - break; - default: - strBody[element.field] = null; - } - }); - return JSON.stringify(strBody, null, 4); - }); - - /** - * - */ - Handlebars.registerHelper('each_compare_field', function(source, compare, options) { - return _handlebarsEachCompared('field', source, compare, options); - }); - - /** - * - */ - Handlebars.registerHelper('each_compare_title', function(source, compare, options) { - return _handlebarsEachCompared('title', source, compare, options); - }); - - /** - * - */ - Handlebars.registerHelper('reformat', function(source, type){ - if (type == 'json') - try { - return JSON.stringify(JSON.parse(source.trim()),null, " "); - } catch(e) { - - } - return source - }); - - /** - * - */ - Handlebars.registerHelper('showDiff', function(source, compare, options) { - var ds = ''; - if(source === compare) { - ds = source; - } else { - if( ! source) - return compare; - - if( ! compare) - return source; - - var d = diffMatchPatch.diff_main(stripHtml(compare), stripHtml(source)); - diffMatchPatch.diff_cleanupSemantic(d); - ds = diffMatchPatch.diff_prettyHtml(d); - ds = ds.replace(/¶/gm, ''); - } - if(options === 'nl2br') - ds = _handlebarsNewlineToBreak(ds); - - return ds; - }); - - /** - * - */ - function _handlebarsEachCompared(fieldname, source, compare, options) - { - var dataList = []; - var index = 0; - if(source) { - source.forEach(function(sourceEntry) { - var found = false; - if (compare) { - compare.forEach(function(compareEntry) { - if(sourceEntry[fieldname] === compareEntry[fieldname]) { - var data = { - typeSame: true, - source: sourceEntry, - compare: compareEntry, - index: index - }; - dataList.push(data); - found = true; - index++; - } - }); - } - if ( ! found) { - var data = { - typeIns: true, - source: sourceEntry, - index: index - }; - dataList.push(data); - index++; - } - }); - } - - if (compare) { - compare.forEach(function(compareEntry) { - var found = false; - if (source) { - source.forEach(function(sourceEntry) { - if(sourceEntry[fieldname] === compareEntry[fieldname]) - found = true; - }); - } - if ( ! found) { - var data = { - typeDel: true, - compare: compareEntry, - index: index - }; - dataList.push(data); - index++; - } - }); - } - - var ret = ''; - var length = dataList.length; - for (var index in dataList) { - if(index == (length - 1)) - dataList[index]['_last'] = true; - ret = ret + options.fn(dataList[index]); - } - return ret; - } - - var diffMatchPatch = new DiffMatchPatch(); - - /** - * Overwrite Colors - */ - DiffMatchPatch.prototype.diff_prettyHtml = function(diffs) { - var html = []; - var pattern_amp = /&/g; - var pattern_lt = //g; - var pattern_para = /\n/g; - for (var x = 0; x < diffs.length; x++) { - var op = diffs[x][0]; // Operation (insert, delete, equal) - var data = diffs[x][1]; // Text of change. - var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<') - .replace(pattern_gt, '>').replace(pattern_para, '¶
'); - switch (op) { - case DIFF_INSERT: - html[x] = '' + text + ''; - break; - case DIFF_DELETE: - html[x] = '' + text + ''; - break; - case DIFF_EQUAL: - html[x] = '' + text + ''; - break; - } - } - return html.join(''); - }; - - /** - * Fixes html after comparison (#506, #538, #616, #825) - */ - function stripHtml(html){ - var div = document.createElement("div"); - div.innerHTML = html; - return div.textContent || div.innerText || ""; - } - - // Exports - return Handlebars; -}); diff --git a/apidoc/utils/send_sample_request.js b/apidoc/utils/send_sample_request.js deleted file mode 100644 index 4d695d14..00000000 --- a/apidoc/utils/send_sample_request.js +++ /dev/null @@ -1,272 +0,0 @@ -define([ - 'jquery', - 'lodash', - './utils/send_sample_request_utils' -], function($, _, utils) { - - var initDynamic = function() { - // Button send - $(".sample-request-send").off("click"); - $(".sample-request-send").on("click", function(e) { - e.preventDefault(); - var $root = $(this).parents("article"); - var group = $root.data("group"); - var name = $root.data("name"); - var version = $root.data("version"); - sendSampleRequest(group, name, version, $(this).data("sample-request-type")); - }); - - // Button clear - $(".sample-request-clear").off("click"); - $(".sample-request-clear").on("click", function(e) { - e.preventDefault(); - var $root = $(this).parents("article"); - var group = $root.data("group"); - var name = $root.data("name"); - var version = $root.data("version"); - clearSampleRequest(group, name, version); - }); - }; // initDynamic - - function sendSampleRequest(group, name, version, type) - { - var $root = $('article[data-group="' + group + '"][data-name="' + name + '"][data-version="' + version + '"]'); - - // Optional header - var header = {}; - $root.find(".sample-request-header:checked").each(function(i, element) { - var group = $(element).data("sample-request-header-group-id"); - $root.find("[data-sample-request-header-group=\"" + group + "\"]").each(function(i, element) { - var key = $(element).data("sample-request-header-name"); - var value = element.value; - if (typeof element.optional === 'undefined') { - element.optional = true; - } - if ( ! element.optional && element.defaultValue !== '') { - value = element.defaultValue; - } - header[key] = value; - }); - }); - - - // create JSON dictionary of parameters - var param = {}; - var paramType = {}; - var bodyFormData = new FormData(); - var bodyJson = ''; - $root.find(".sample-request-param:checked").each(function(i, element) { - var group = $(element).data("sample-request-param-group-id"); - var contentType = $(element).nextAll('.sample-header-content-type-switch').first().val(); - if (contentType == "body-json"){ - $root.find("[data-sample-request-body-group=\"" + group + "\"]").not(function(){ - return $(this).val() == "" && $(this).is("[data-sample-request-param-optional='true']"); - }).each(function(i, element) { - if (isJson(element.value)){ - header['Content-Type'] = 'application/json'; - bodyJson = element.value; - } - }); - }else { - $root.find("[data-sample-request-param-group=\"" + group + "\"]").not(function(){ - return $(this).val() == "" && $(this).is("[data-sample-request-param-optional='true']"); - }).each(function(i, element) { - var key = $(element).data("sample-request-param-name"); - var value = element.value; - if ( ! element.optional && element.defaultValue !== '') { - value = element.defaultValue; - } - if (contentType == "body-form-data"){ - header['Content-Type'] = 'multipart/form-data' - if (element.type == "file") { - value = element.files[0]; - } - bodyFormData.append(key,value); - }else { - param[key] = value; - paramType[key] = $(element).next().text(); - } - }); - } - }); - - // grab user-inputted URL - var url = $root.find(".sample-request-url").val(); - - //Convert {param} form to :param - url = utils.convertPathParams(url); - - // Insert url parameter - var pattern = pathToRegexp(url, null); - var matches = pattern.exec(url); - for (var i = 1; i < matches.length; i++) { - var key = matches[i].substr(1); - var optional = false - if (key[key.length - 1] === '?') { - optional = true; - key = key.substr(0, key.length - 1); - } - if (param[key] !== undefined) { - url = url.replace(matches[i], encodeURIComponent(param[key])); - - // remove URL parameters from list - delete param[key]; - } else if (optional) { - // if parameter is optional denoted by ending '?' in param (:param?) - // and no parameter is given, replace parameter with empty string instead - url = url.replace(matches[i], ''); - delete param[key]; - } - } // for - - //handle nested objects and parsing fields - param = utils.handleNestedAndParsingFields(param, paramType); - - //add url search parameter - if (header['Content-Type'] == 'application/json') { - if (bodyJson) { - // bodyJson is set to value if request body: 'body/json' was selected and manual json was input - // in this case, use the given bodyJson and add other params in query string - url = url + encodeSearchParams(param); - param = bodyJson; - } else { - // bodyJson not set, but Content-Type: application/json header was set. In this case, send parameters - // as JSON body. First, try parsing fields of object with given paramType definition so that the json - // is valid against the parameter spec (e.g. Boolean params are boolean instead of strings in final json) - param = utils.tryParsingWithTypes(param, paramType); - param = JSON.stringify(param); - } - }else if (header['Content-Type'] == 'multipart/form-data'){ - url = url + encodeSearchParams(param); - param = bodyFormData; - } - - $root.find(".sample-request-response").fadeTo(250, 1); - $root.find(".sample-request-response-json").html("Loading..."); - refreshScrollSpy(); - - // send AJAX request, catch success or error callback - var ajaxRequest = { - url : url, - headers : header, - data : param, - type : type.toUpperCase(), - success : displaySuccess, - error : displayError - }; - - if(header['Content-Type'] == 'multipart/form-data'){ - delete ajaxRequest.headers['Content-Type']; - ajaxRequest.contentType=false; - ajaxRequest.processData=false; - } - $.ajax(ajaxRequest); - - - function displaySuccess(data, status, jqXHR) { - var jsonResponse; - try { - jsonResponse = JSON.parse(jqXHR.responseText); - jsonResponse = JSON.stringify(jsonResponse, null, 4); - } catch (e) { - jsonResponse = jqXHR.responseText; - } - $root.find(".sample-request-response-json").text(jsonResponse); - refreshScrollSpy(); - }; - - function displayError(jqXHR, textStatus, error) { - var message = "Error " + jqXHR.status + ": " + error; - var jsonResponse; - try { - jsonResponse = JSON.parse(jqXHR.responseText); - jsonResponse = JSON.stringify(jsonResponse, null, 4); - } catch (e) { - jsonResponse = jqXHR.responseText; - } - - if (jsonResponse) - message += "\n" + jsonResponse; - - // flicker on previous error to make clear that there is a new response - if($root.find(".sample-request-response").is(":visible")) - $root.find(".sample-request-response").fadeTo(1, 0.1); - - $root.find(".sample-request-response").fadeTo(250, 1); - $root.find(".sample-request-response-json").text(message); - refreshScrollSpy(); - }; - } - - function clearSampleRequest(group, name, version) - { - var $root = $('article[data-group="' + group + '"][data-name="' + name + '"][data-version="' + version + '"]'); - - // hide sample response - $root.find(".sample-request-response-json").html(""); - $root.find(".sample-request-response").hide(); - - // reset value of parameters - $root.find(".sample-request-param").each(function(i, element) { - element.value = ""; - }); - - // restore default URL - var $urlElement = $root.find(".sample-request-url"); - $urlElement.val($urlElement.prop("defaultValue")); - - refreshScrollSpy(); - } - - function refreshScrollSpy() - { - $('[data-spy="scroll"]').each(function () { - $(this).scrollspy("refresh"); - }); - } - - function escapeHtml(str) { - var div = document.createElement("div"); - div.appendChild(document.createTextNode(str)); - return div.innerHTML; - } - - - /** - * is Json - */ - function isJson(str) { - if (typeof str == 'string') { - try { - var obj=JSON.parse(str); - if(typeof obj == 'object' && obj ){ - return true; - }else{ - return false; - } - } catch(e) { - return false; - } - } - } - - /** - * encode Search Params - */ - function encodeSearchParams(obj) { - const params = []; - Object.keys(obj).forEach((key) => { - let value = obj[key]; - params.push([key, encodeURIComponent(value)].join('=')); - }) - return params.length === 0 ? '' : '?' + params.join('&'); - } - - /** - * Exports. - */ - return { - initDynamic: initDynamic - }; - -}); diff --git a/apidoc/utils/send_sample_request_utils.js b/apidoc/utils/send_sample_request_utils.js deleted file mode 100644 index fbda1ffd..00000000 --- a/apidoc/utils/send_sample_request_utils.js +++ /dev/null @@ -1,97 +0,0 @@ -//this block is used to make this module works with Node (CommonJS module format) -if (typeof define !== 'function') { - var define = require('amdefine')(module) -} - -define(['lodash'], function (_) { - - var log = console; - - function handleNestedFields(object, key, params, paramType) { - var attributes = key.split('.'); - var field = attributes[0]; - params.push(field); - if (attributes.length > 1 && paramType[params.join('.')] == 'Object') { - var nestedField = attributes.slice(1).join('.'); - if (!object[field]) - object[field] = {}; - if (typeof object[field] == 'object') { - object[field][nestedField] = object[key]; - delete object[key]; - handleNestedFields(object[field], nestedField, params, paramType); - } - } - } - - function handleNestedFieldsForAllParams(param, paramType) { - var result = Object.assign({}, param); - Object.keys(result).forEach(function (key) { - handleNestedFields(result, key, [], paramType); - }); - return result - } - - function handleArraysAndObjectFields(param, paramType) { - var result = Object.assign({}, param); - Object.keys(paramType).forEach(function (key) { - if (result[key] && (paramType[key].endsWith('[]') || paramType[key] === 'Object')) { - try { - result[key] = JSON.parse(result[key]); - } catch (e) {;} - } - }); - return result - } - - function tryParsingAsType(object, path, type) { - var val = _.get(object, path); - if (val !== undefined) { - if (type === 'Boolean') { - if (val === 'true') { - _.set(object, path, true); - } else if (val === 'false') { - _.set(object, path, false); - } else { - log.warn('Failed to parse object value at path [' + path + ']. Value: (' + val + '). Type: (' + type + ')'); - } - } else if (type === 'Number') { - var parsedInt = parseInt(val, 10); - if (!_.isNaN(parsedInt)) { - _.set(object, path, parsedInt); - } else { - log.warn('Failed to parse object value at path [' + path + ']. Value: (' + val + '). Type: (' + type + ')'); - } - } - } - } - - function handleNestedAndParsingFields(param, paramType) { - var result = handleArraysAndObjectFields(param, paramType); - result = handleNestedFieldsForAllParams(result, paramType); - return result; - } - - function tryParsingWithTypes(param, paramType) { - var result = Object.assign({}, param); - Object.keys(paramType).forEach(function (key) { - tryParsingAsType(result, key, paramType[key]); - }); - return result; - } - - // Converts path params in the {param} format to the accepted :param format, used before inserting the URL params. - function convertPathParams(url) { - return url.replace(/{(.+?)}/g, ':$1'); - } - - function setLogger(logger) { - log = logger; - } - - return { - handleNestedAndParsingFields, - convertPathParams, - tryParsingWithTypes, - setLogger - }; -}); diff --git a/apidoc/vendor/bootstrap.min.css b/apidoc/vendor/bootstrap.min.css deleted file mode 100644 index 5b96335f..00000000 --- a/apidoc/vendor/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:"Glyphicons Halflings";src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format("embedded-opentype"),url(../fonts/glyphicons-halflings-regular.woff2) format("woff2"),url(../fonts/glyphicons-halflings-regular.woff) format("woff"),url(../fonts/glyphicons-halflings-regular.ttf) format("truetype"),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:""}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*=col-]{padding-right:0;padding-left:0}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s,-webkit-box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;background-image:none;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;background-image:none;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;background-image:none;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;background-image:none;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;background-image:none;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;background-image:none;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out,-o-transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out,-o-transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);left:0}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);left:0}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/apidoc/vendor/bootstrap.min.js b/apidoc/vendor/bootstrap.min.js deleted file mode 100644 index 6290896a..00000000 --- a/apidoc/vendor/bootstrap.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");!function(t){"use strict";var e=jQuery.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||3this.$items.length-1||t<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){e.to(t)}):i==t?this.pause().cycle():this.slide(idocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},s.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},s.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0},sanitize:!0,sanitizeFn:null,whiteList:t},m.prototype.init=function(t,e,i){if(this.enabled=!0,this.type=t,this.$element=g(e),this.options=this.getOptions(i),this.$viewport=this.options.viewport&&g(document).find(g.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var o=this.options.trigger.split(" "),n=o.length;n--;){var s=o[n];if("click"==s)this.$element.on("click."+this.type,this.options.selector,g.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",r="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,g.proxy(this.enter,this)),this.$element.on(r+"."+this.type,this.options.selector,g.proxy(this.leave,this))}}this.options.selector?this._options=g.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},m.prototype.getDefaults=function(){return m.DEFAULTS},m.prototype.getOptions=function(t){var e=this.$element.data();for(var i in e)e.hasOwnProperty(i)&&-1!==g.inArray(i,o)&&delete e[i];return(t=g.extend({},this.getDefaults(),e,t)).delay&&"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t.sanitize&&(t.template=n(t.template,t.whiteList,t.sanitizeFn)),t},m.prototype.getDelegateOptions=function(){var i={},o=this.getDefaults();return this._options&&g.each(this._options,function(t,e){o[t]!=e&&(i[t]=e)}),i},m.prototype.enter=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusin"==t.type?"focus":"hover"]=!0),e.tip().hasClass("in")||"in"==e.hoverState)e.hoverState="in";else{if(clearTimeout(e.timeout),e.hoverState="in",!e.options.delay||!e.options.delay.show)return e.show();e.timeout=setTimeout(function(){"in"==e.hoverState&&e.show()},e.options.delay.show)}},m.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},m.prototype.leave=function(t){var e=t instanceof this.constructor?t:g(t.currentTarget).data("bs."+this.type);if(e||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e)),t instanceof g.Event&&(e.inState["focusout"==t.type?"focus":"hover"]=!1),!e.isInStateTrue()){if(clearTimeout(e.timeout),e.hoverState="out",!e.options.delay||!e.options.delay.hide)return e.hide();e.timeout=setTimeout(function(){"out"==e.hoverState&&e.hide()},e.options.delay.hide)}},m.prototype.show=function(){var t=g.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var e=g.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!e)return;var i=this,o=this.tip(),n=this.getUID(this.type);this.setContent(),o.attr("id",n),this.$element.attr("aria-describedby",n),this.options.animation&&o.addClass("fade");var s="function"==typeof this.options.placement?this.options.placement.call(this,o[0],this.$element[0]):this.options.placement,a=/\s?auto?\s?/i,r=a.test(s);r&&(s=s.replace(a,"")||"top"),o.detach().css({top:0,left:0,display:"block"}).addClass(s).data("bs."+this.type,this),this.options.container?o.appendTo(g(document).find(this.options.container)):o.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),h=o[0].offsetWidth,d=o[0].offsetHeight;if(r){var p=s,c=this.getPosition(this.$viewport);s="bottom"==s&&l.bottom+d>c.bottom?"top":"top"==s&&l.top-dc.width?"left":"left"==s&&l.left-ha.top+a.height&&(n.top=a.top+a.height-l)}else{var h=e.left-s,d=e.left+s+i;ha.right&&(n.left=a.left+a.width-d)}return n},m.prototype.getTitle=function(){var t=this.$element,e=this.options;return t.attr("data-original-title")||("function"==typeof e.title?e.title.call(t[0]):e.title)},m.prototype.getUID=function(t){for(;t+=~~(1e6*Math.random()),document.getElementById(t););return t},m.prototype.tip=function(){if(!this.$tip&&(this.$tip=g(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},m.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},m.prototype.enable=function(){this.enabled=!0},m.prototype.disable=function(){this.enabled=!1},m.prototype.toggleEnabled=function(){this.enabled=!this.enabled},m.prototype.toggle=function(t){var e=this;t&&((e=g(t.currentTarget).data("bs."+this.type))||(e=new this.constructor(t.currentTarget,this.getDelegateOptions()),g(t.currentTarget).data("bs."+this.type,e))),t?(e.inState.click=!e.inState.click,e.isInStateTrue()?e.enter(e):e.leave(e)):e.tip().hasClass("in")?e.leave(e):e.enter(e)},m.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})},m.prototype.sanitizeHtml=function(t){return n(t,this.options.whiteList,this.options.sanitizeFn)};var e=g.fn.tooltip;g.fn.tooltip=function i(o){return this.each(function(){var t=g(this),e=t.data("bs.tooltip"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.tooltip",e=new m(this,i)),"string"==typeof o&&e[o]())})},g.fn.tooltip.Constructor=m,g.fn.tooltip.noConflict=function(){return g.fn.tooltip=e,this}}(jQuery),function(n){"use strict";var s=function(t,e){this.init("popover",t,e)};if(!n.fn.tooltip)throw new Error("Popover requires tooltip.js");s.VERSION="3.4.1",s.DEFAULTS=n.extend({},n.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),((s.prototype=n.extend({},n.fn.tooltip.Constructor.prototype)).constructor=s).prototype.getDefaults=function(){return s.DEFAULTS},s.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),i=this.getContent();if(this.options.html){var o=typeof i;this.options.sanitize&&(e=this.sanitizeHtml(e),"string"===o&&(i=this.sanitizeHtml(i))),t.find(".popover-title").html(e),t.find(".popover-content").children().detach().end()["string"===o?"html":"append"](i)}else t.find(".popover-title").text(e),t.find(".popover-content").children().detach().end().text(i);t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},s.prototype.hasContent=function(){return this.getTitle()||this.getContent()},s.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},s.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var t=n.fn.popover;n.fn.popover=function e(o){return this.each(function(){var t=n(this),e=t.data("bs.popover"),i="object"==typeof o&&o;!e&&/destroy|hide/.test(o)||(e||t.data("bs.popover",e=new s(this,i)),"string"==typeof o&&e[o]())})},n.fn.popover.Constructor=s,n.fn.popover.noConflict=function(){return n.fn.popover=t,this}}(jQuery),function(s){"use strict";function n(t,e){this.$body=s(document.body),this.$scrollElement=s(t).is(document.body)?s(window):s(t),this.options=s.extend({},n.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",s.proxy(this.process,this)),this.refresh(),this.process()}function e(o){return this.each(function(){var t=s(this),e=t.data("bs.scrollspy"),i="object"==typeof o&&o;e||t.data("bs.scrollspy",e=new n(this,i)),"string"==typeof o&&e[o]()})}n.VERSION="3.4.1",n.DEFAULTS={offset:10},n.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},n.prototype.refresh=function(){var t=this,o="offset",n=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),s.isWindow(this.$scrollElement[0])||(o="position",n=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=s(this),e=t.data("target")||t.attr("href"),i=/^#./.test(e)&&s(e);return i&&i.length&&i.is(":visible")&&[[i[o]().top+n,e]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},n.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),o=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,s=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),o<=e)return a!=(t=s[s.length-1])&&this.activate(t);if(a&&e=n[t]&&(n[t+1]===undefined||e .active"),n=i&&r.support.transition&&(o.length&&o.hasClass("fade")||!!e.find("> .fade").length);function s(){o.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),t.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),n?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu").length&&t.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),i&&i()}o.length&&n?o.one("bsTransitionEnd",s).emulateTransitionEnd(a.TRANSITION_DURATION):s(),o.removeClass("in")};var t=r.fn.tab;r.fn.tab=e,r.fn.tab.Constructor=a,r.fn.tab.noConflict=function(){return r.fn.tab=t,this};var i=function(t){t.preventDefault(),e.call(r(this),"show")};r(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',i).on("click.bs.tab.data-api",'[data-toggle="pill"]',i)}(jQuery),function(l){"use strict";var h=function(t,e){this.options=l.extend({},h.DEFAULTS,e);var i=this.options.target===h.DEFAULTS.target?l(this.options.target):l(document).find(this.options.target);this.$target=i.on("scroll.bs.affix.data-api",l.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",l.proxy(this.checkPositionWithEventLoop,this)),this.$element=l(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};function i(o){return this.each(function(){var t=l(this),e=t.data("bs.affix"),i="object"==typeof o&&o;e||t.data("bs.affix",e=new h(this,i)),"string"==typeof o&&e[o]()})}h.VERSION="3.4.1",h.RESET="affix affix-top affix-bottom",h.DEFAULTS={offset:0,target:window},h.prototype.getState=function(t,e,i,o){var n=this.$target.scrollTop(),s=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return n=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a, -b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a}; -diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l= -u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]}; -diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)}; -diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;fd?a=a.substring(c-d):c=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null; -var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.lengthd[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]}; -diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}}; -diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_); -return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]= -h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/; -diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;fb)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)}; -diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=//g,f=/\n/g,g=0;g");switch(h){case 1:b[g]=''+j+"";break;case -1:b[g]=''+j+"";break;case 0:b[g]=""+j+""}}return b.join("")}; -diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cthis.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h}; -diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c=2*this.Patch_Margin&& -e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;cthis.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g); -if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;ie[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0, -c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c}; -diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&& -(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};b.REVISION_CHANGES=q;var r="[object Object]";d.prototype={constructor:d,logger:l["default"],log:l["default"].log,registerHelper:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){if(f.toString.call(a)===r)f.extend(this.partials,a);else{if("undefined"==typeof b)throw new h["default"]('Attempting to register a partial called "'+a+'" as undefined');this.partials[a]=b}},unregisterPartial:function(a){delete this.partials[a]},registerDecorator:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple decorators");f.extend(this.decorators,a)}else this.decorators[a]=b},unregisterDecorator:function(a){delete this.decorators[a]},resetLoggedPropertyAccesses:function(){m.resetLoggedProperties()}};var s=l["default"].log;b.log=s,b.createFrame=f.createFrame,b.logger=l["default"]},function(a,b){"use strict";function c(a){return k[a]}function d(a){for(var b=1;b":">",'"':""","'":"'","`":"`","=":"="},l=/[&<>"'`=]/g,m=/[&<>"'`=]/,n=Object.prototype.toString;b.toString=n;var o=function(a){return"function"==typeof a};o(/x/)&&(b.isFunction=o=function(a){return"function"==typeof a&&"[object Function]"===n.call(a)}),b.isFunction=o;var p=Array.isArray||function(a){return!(!a||"object"!=typeof a)&&"[object Array]"===n.call(a)};b.isArray=p},function(a,b,c){"use strict";function d(a,b){var c=b&&b.loc,g=void 0,h=void 0,i=void 0,j=void 0;c&&(g=c.start.line,h=c.end.line,i=c.start.column,j=c.end.column,a+=" - "+g+":"+i);for(var k=Error.prototype.constructor.call(this,a),l=0;l0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):e(this);if(c.data&&c.ids){var g=d.createFrame(c.data);g.contextPath=d.appendContextPath(c.data.contextPath,c.name),c={data:g}}return f(b,c)})},a.exports=b["default"]},function(a,b,c){(function(d){"use strict";var e=c(13)["default"],f=c(1)["default"];b.__esModule=!0;var g=c(5),h=c(6),i=f(h);b["default"]=function(a){a.registerHelper("each",function(a,b){function c(b,c,d){l&&(l.key=b,l.index=c,l.first=0===c,l.last=!!d,m&&(l.contextPath=m+b)),k+=f(a[b],{data:l,blockParams:g.blockParams([a[b],b],[m+b,null])})}if(!b)throw new i["default"]("Must pass iterator to #each");var f=b.fn,h=b.inverse,j=0,k="",l=void 0,m=void 0;if(b.data&&b.ids&&(m=g.appendContextPath(b.data.contextPath,b.ids[0])+"."),g.isFunction(a)&&(a=a.call(this)),b.data&&(l=g.createFrame(b.data)),a&&"object"==typeof a)if(g.isArray(a))for(var n=a.length;j=0?b:parseInt(a,10)}return a},log:function(a){if(a=e.lookupLevel(a),"undefined"!=typeof console&&e.lookupLevel(e.level)<=a){var b=e.methodMap[a];console[b]||(b="log");for(var c=arguments.length,d=Array(c>1?c-1:0),f=1;f=v.LAST_COMPATIBLE_COMPILER_REVISION&&b<=v.COMPILER_REVISION)){if(b2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;gb[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substring(a,b.yyleng-c+a)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 15;break;case 1:return 15;case 2:return this.popState(),15;case 3:return this.begin("raw"),15;case 4:return this.popState(),"raw"===this.conditionStack[this.conditionStack.length-1]?15:(e(5,9),"END_RAW_BLOCK");case 5:return 15;case 6:return this.popState(),14;case 7:return 65;case 8:return 68;case 9:return 19;case 10:return this.popState(),this.begin("raw"),23;case 11:return 55;case 12:return 60;case 13:return 29;case 14:return 47;case 15:return this.popState(),44;case 16:return this.popState(),44;case 17:return 34;case 18:return 39;case 19:return 51;case 20:return 48;case 21:this.unput(b.yytext),this.popState(),this.begin("com");break;case 22:return this.popState(),14;case 23:return 48;case 24:return 73;case 25:return 72;case 26:return 72;case 27:return 87;case 28:break;case 29:return this.popState(),54;case 30:return this.popState(),33;case 31:return b.yytext=e(1,2).replace(/\\"/g,'"'),80;case 32:return b.yytext=e(1,2).replace(/\\'/g,"'"),80;case 33:return 85;case 34:return 82;case 35:return 82;case 36:return 83;case 37:return 84;case 38:return 81;case 39:return 75;case 40:return 77;case 41:return 72;case 42:return b.yytext=b.yytext.replace(/\\([\\\]])/g,"$1"),72;case 43:return"INVALID";case 44:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{(?=[^\/]))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]+?(?=(\{\{\{\{)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#>)/,/^(?:\{\{(~)?#\*?)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?\*?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[(\\\]|[^\]])*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[6],inclusive:!1},raw:{rules:[3,4,5],inclusive:!1},INITIAL:{rules:[0,1,44],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();b["default"]=c,a.exports=b["default"]},function(a,b,c){"use strict";function d(){var a=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.options=a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"ContentStatement"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"ContentStatement"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"ContentStatement"===d.type&&(c||!d.rightStripped)){var e=d.value;d.value=d.value.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.value!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"ContentStatement"===d.type&&(c||!d.leftStripped)){var e=d.value;return d.value=d.value.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.value!==e,d.leftStripped}}var i=c(1)["default"];b.__esModule=!0;var j=c(49),k=i(j);d.prototype=new k["default"],d.prototype.Program=function(a){var b=!this.options.ignoreStandalone,c=!this.isRootSeen;this.isRootSeen=!0;for(var d=a.body,i=0,j=d.length;i0)throw new q["default"]("Invalid path: "+d,{loc:c});".."===i&&f++}}return{type:"PathExpression",data:a,depth:f,parts:e,original:d,loc:c}}function j(a,b,c,d,e,f){var g=d.charAt(3)||d.charAt(2),h="{"!==g&&"&"!==g,i=/\*/.test(d);return{type:i?"Decorator":"MustacheStatement",path:a,params:b,hash:c,escaped:h,strip:e,loc:this.locInfo(f)}}function k(a,b,c,e){d(a,c),e=this.locInfo(e);var f={type:"Program",body:b,strip:{},loc:e};return{type:"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:f,openStrip:{},inverseStrip:{},closeStrip:{},loc:e}}function l(a,b,c,e,f,g){e&&e.path&&d(a,e);var h=/\*/.test(a.open);b.blockParams=a.blockParams;var i=void 0,j=void 0;if(c){if(h)throw new q["default"]("Unexpected inverse block on decorator",c);c.chain&&(c.program.body[0].closeStrip=e.strip),j=c.strip,i=c.program}return f&&(f=i,i=b,b=f),{type:h?"DecoratorBlock":"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:b,inverse:i,openStrip:a.strip,inverseStrip:j,closeStrip:e&&e.strip,loc:this.locInfo(g)}}function m(a,b){if(!b&&a.length){var c=a[0].loc,d=a[a.length-1].loc;c&&d&&(b={source:c.source,start:{line:c.start.line,column:c.start.column},end:{line:d.end.line,column:d.end.column}})}return{type:"Program",body:a,strip:{},loc:b}}function n(a,b,c,e){return d(a,c),{type:"PartialBlockStatement",name:a.path,params:a.params,hash:a.hash,program:b,openStrip:a.strip,closeStrip:c&&c.strip,loc:this.locInfo(e)}}var o=c(1)["default"];b.__esModule=!0,b.SourceLocation=e,b.id=f,b.stripFlags=g,b.stripComment=h,b.preparePath=i,b.prepareMustache=j,b.prepareRawBlock=k,b.prepareBlock=l,b.prepareProgram=m,b.preparePartialBlock=n;var p=c(6),q=o(p)},function(a,b,c){"use strict";function d(){}function e(a,b,c){if(null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a,b),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function f(a,b,c){function d(){var d=c.parse(a,b),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}function e(a,b){return f||(f=d()),f.call(this,a,b)}if(void 0===b&&(b={}),null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=m.extend({},b),"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var f=void 0;return e._setup=function(a){return f||(f=d()),f._setup(a)},e._child=function(a,b,c,e){return f||(f=d()),f._child(a,b,c,e)},e}function g(a,b){if(a===b)return!0;if(m.isArray(a)&&m.isArray(b)&&a.length===b.length){for(var c=0;c1)throw new l["default"]("Unsupported number of partial arguments: "+c.length,a);c.length||(this.options.explicitPartialContext?this.opcode("pushLiteral","undefined"):c.push({type:"PathExpression",parts:[],depth:0});var d=a.name.original,e="SubExpression"===a.name.type;e&&this.accept(a.name),this.setupFullMustacheParams(a,b,void 0,!0);var f=a.indent||"";this.options.preventIndent&&f&&(this.opcode("appendContent",f),f=""),this.opcode("invokePartial",e,d,f),this.opcode("append")},PartialBlockStatement:function(a){this.PartialStatement(a)},MustacheStatement:function(a){this.SubExpression(a),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},Decorator:function(a){this.DecoratorBlock(a)},ContentStatement:function(a){a.value&&this.opcode("appendContent",a.value)},CommentStatement:function(){},SubExpression:function(a){h(a);var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ambiguousSexpr:function(a,b,c){var d=a.path,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),d.strict=!0,this.accept(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.path;b.strict=!0,this.accept(b),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.path,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new l["default"]("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.strict=!0,e.falsy=!0,this.accept(e),this.opcode("invokeHelper",d.length,e.original,o["default"].helpers.simpleId(e))}},PathExpression:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0],c=o["default"].helpers.scopedId(a),d=!a.depth&&!c&&this.blockParamIndex(b);d?this.opcode("lookupBlockParam",d,a.parts):b?a.data?(this.options.data=!0,this.opcode("lookupData",a.depth,a.parts,a.strict)):this.opcode("lookupOnContext",a.parts,a.falsy,a.strict,c):this.opcode("pushContext")},StringLiteral:function(a){this.opcode("pushString",a.value)},NumberLiteral:function(a){this.opcode("pushLiteral",a.value)},BooleanLiteral:function(a){this.opcode("pushLiteral",a.value)},UndefinedLiteral:function(){this.opcode("pushLiteral","undefined")},NullLiteral:function(){this.opcode("pushLiteral","null")},Hash:function(a){var b=a.pairs,c=0,d=b.length;for(this.opcode("pushHash");c=0)return[b,e]}}}},function(a,b,c){"use strict";function d(a){this.value=a}function e(){}function f(a,b,c,d){var e=b.popStack(),f=0,g=c.length;for(a&&g--;f0&&(c+=", "+d.join(", "));var e=0;g(this.aliases).forEach(function(a){var d=b.aliases[a];d.children&&d.referenceCount>1&&(c+=", alias"+ ++e+"="+a,d.children[0]="alias"+e)}),this.lookupPropertyFunctionIsUsed&&(c+=", "+this.lookupPropertyFunctionVarDeclaration());var f=["container","depth0","helpers","partials","data"];(this.useBlockParams||this.useDepths)&&f.push("blockParams"),this.useDepths&&f.push("depths");var h=this.mergeSource(c);return a?(f.push(h),Function.apply(this,f)):this.source.wrap(["function(",f.join(","),") {\n ",h,"}"])},mergeSource:function(a){var b=this.environment.isSimple,c=!this.forceBuffer,d=void 0,e=void 0,f=void 0,g=void 0;return this.source.each(function(a){a.appendToBuffer?(f?a.prepend(" + "):f=a,g=a):(f&&(e?f.prepend("buffer += "):d=!0,g.add(";"),f=g=void 0),e=!0,b||(c=!1))}),c?f?(f.prepend("return "),g.add(";")):e||this.source.push('return "";'):(a+=", buffer = "+(d?"":this.initializeBuffer()),f?(f.prepend("return buffer + "),g.add(";")):this.source.push("return buffer;")),a&&this.source.prepend("var "+a.substring(2)+(d?"":";\n")),this.source.merge()},lookupPropertyFunctionVarDeclaration:function(){return"\n lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n }\n ".trim()},blockValue:function(a){var b=this.aliasable("container.hooks.blockHelperMissing"),c=[this.contextName(0)];this.setupHelperArgs(a,0,c);var d=this.popStack();c.splice(1,0,d),this.push(this.source.functionCall(b,"call",c))},ambiguousBlockValue:function(){var a=this.aliasable("container.hooks.blockHelperMissing"),b=[this.contextName(0)];this.setupHelperArgs("",0,b,!0),this.flushInline();var c=this.topStack();b.splice(1,0,c),this.pushSource(["if (!",this.lastHelper,") { ",c," = ",this.source.functionCall(a,"call",b),"}"])},appendContent:function(a){this.pendingContent?a=this.pendingContent+a:this.pendingLocation=this.source.currentLocation,this.pendingContent=a},append:function(){if(this.isInline())this.replaceStack(function(a){return[" != null ? ",a,' : ""']}),this.pushSource(this.appendToBuffer(this.popStack()));else{var a=this.popStack();this.pushSource(["if (",a," != null) { ",this.appendToBuffer(a,void 0,!0)," }"]),this.environment.isSimple&&this.pushSource(["else { ",this.appendToBuffer("''",void 0,!0)," }"])}},appendEscaped:function(){this.pushSource(this.appendToBuffer([this.aliasable("container.escapeExpression"),"(",this.popStack(),")"]))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c,d){var e=0;d||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[e++])),this.resolvePath("context",a,e,b,c)},lookupBlockParam:function(a,b){this.useBlockParams=!0,this.push(["blockParams[",a[0],"][",a[1],"]"]),this.resolvePath("context",b,1)},lookupData:function(a,b,c){a?this.pushStackLiteral("container.data(data, "+a+")"):this.pushStackLiteral("data"),this.resolvePath("data",b,0,!0,c)},resolvePath:function(a,b,c,d,e){var g=this;if(this.options.strict||this.options.assumeObjects)return void this.push(f(this.options.strict&&e,this,b,a));for(var h=b.length;cthis.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;this.inlineStack=[];for(var b=0,c=a.length;b+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")}),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0r.page,h=new s(a[f],d,e),r.items.push(h),c.push(h)}return r.update(),c}},this.show=function(a,b){return this.i=a,this.page=b,r.update(),r},this.remove=function(a,b,c){for(var d=0,e=0,f=r.items.length;e-1&&c.splice(d,1),r},this.trigger=function(a){for(var b=r.handlers[a].length;b--;)r.handlers[a][b](r);return r},this.reset={filter:function(){for(var a=r.items,b=a.length;b--;)a[b].filtered=!1;return r},search:function(){for(var a=r.items,b=a.length;b--;)a[b].found=!1;return r}},this.update=function(){var a=r.items,b=a.length;r.visibleItems=[],r.matchingItems=[],r.templater.clear();for(var c=0;c=r.i&&r.visibleItems.length0?setTimeout(function(){b(c,d,e)},1):(a.update(),d(e))};return b}},{}],3:[function(a,b,c){b.exports=function(a){return a.handlers.filterStart=a.handlers.filterStart||[],a.handlers.filterComplete=a.handlers.filterComplete||[],function(b){if(a.trigger("filterStart"),a.i=1,a.reset.filter(),void 0===b)a.filtered=!1;else{a.filtered=!0;for(var c=a.items,d=0,e=c.length;d0?setTimeout(function(){f(a,c)},1):(b.update(),b.trigger("parseComplete"))};return b.handlers.parseComplete=b.handlers.parseComplete||[],function(){var a=d(b.list),c=b.valueNames;b.indexAsync?f(a,c):e(a,c)}}},{"./item":4}],6:[function(a,b,c){b.exports=function(a){var b,c,d,e,f={resetList:function(){a.i=1,a.templater.clear(),e=void 0},setOptions:function(a){2==a.length&&a[1]instanceof Array?c=a[1]:2==a.length&&"function"==typeof a[1]?(c=void 0,e=a[1]):3==a.length?(c=a[1],e=a[2]):c=void 0},setColumns:function(){0!==a.items.length&&void 0===c&&(c=void 0===a.searchColumns?f.toArray(a.items[0].values()):a.searchColumns)},setSearchString:function(b){b=a.utils.toString(b).toLowerCase(),b=b.replace(/[-[\]{}()*+?.,\\^$|#]/g,"\\$&"),d=b},toArray:function(a){var b=[];for(var c in a)b.push(c);return b}},g={list:function(){for(var b=0,c=a.items.length;b-1))},reset:function(){a.reset.search(),a.searched=!1}},h=function(b){return a.trigger("searchStart"),f.resetList(),f.setSearchString(b),f.setOptions(arguments),f.setColumns(),""===d?g.reset():(a.searched=!0,e?e(d,c):g.list()),a.update(),a.trigger("searchComplete"),a.visibleItems};return a.handlers.searchStart=a.handlers.searchStart||[],a.handlers.searchComplete=a.handlers.searchComplete||[],a.utils.events.bind(a.utils.getByClass(a.listContainer,a.searchClass),"keyup",function(b){var c=b.target||b.srcElement,d=""===c.value&&!a.searched;d||h(c.value)}),a.utils.events.bind(a.utils.getByClass(a.listContainer,a.searchClass),"input",function(a){var b=a.target||a.srcElement;""===b.value&&h("")}),h}},{}],7:[function(a,b,c){b.exports=function(a){a.sortFunction=a.sortFunction||function(b,c,d){return d.desc="desc"==d.order,a.utils.naturalSort(b.values()[d.valueName],c.values()[d.valueName],d)};var b={els:void 0,clear:function(){for(var c=0,d=b.els.length;c]/g.exec(b)){var f=document.createElement("tbody");return f.innerHTML=b,f.firstChild}if(b.indexOf("<")!==-1){var g=document.createElement("div");return g.innerHTML=b,g.firstChild}var h=document.getElementById(a.item);if(h)return h}},this.get=function(b,d){c.create(b);for(var e={},f=0,g=d.length;f=1;)a.list.removeChild(a.list.firstChild)},d()};b.exports=function(a){return new d(a)}},{}],9:[function(a,b,c){function d(a){if(!a||!a.nodeType)throw new Error("A DOM element reference is required");this.el=a,this.list=a.classList}var e=a("./index-of"),f=/\s+/,g=Object.prototype.toString;b.exports=function(a){return new d(a)},d.prototype.add=function(a){if(this.list)return this.list.add(a),this;var b=this.array(),c=e(b,a);return~c||b.push(a),this.el.className=b.join(" "),this},d.prototype.remove=function(a){if("[object RegExp]"==g.call(a))return this.removeMatching(a);if(this.list)return this.list.remove(a),this;var b=this.array(),c=e(b,a);return~c&&b.splice(c,1),this.el.className=b.join(" "),this},d.prototype.removeMatching=function(a){for(var b=this.array(),c=0;cs)return 1}for(var u=0,v=p.length,w=q.length,x=Math.max(v,w);ue)return 1}return 0}},{}],16:[function(a,b,c){function d(a){return"[object Array]"===Object.prototype.toString.call(a)}b.exports=function(a){if("undefined"==typeof a)return[];if(null===a)return[null];if(a===window)return[window];if("string"==typeof a)return[a];if(d(a))return a;if("number"!=typeof a.length)return[a];if("function"==typeof a&&a instanceof Function)return[a];for(var b=[],c=0;ca))return false; -if((f=u.get(t))&&u.get(e))return f==e;var f=-1,s=true,l=2&r?new y:Ut;for(u.set(t,e),u.set(e,t);++f=t}function vt(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function gt(t){return null!=t&&typeof t=="object"}function dt(t){return typeof t=="number"||gt(t)&&"[object Number]"==k(t)}function At(t){return typeof t=="symbol"||gt(t)&&"[object Symbol]"==k(t)}function wt(t){return null==t?"":V(t)}function mt(t,e,r){return t=null==t?Ut:S(t,e),t===Ut?r:t}function Ot(t,e){var r;if(r=null!=t){r=t;var n;n=C(e,r);for(var o=-1,c=n.length,u=false;++ot)&&(t==e.length-1?e.pop():je.call(e,t,1),--this.size,true)},h.prototype.get=function(t){var e=this.__data__;return t=g(e,t),0>t?Ut:e[t][1]},h.prototype.has=function(t){return-1n?(++this.size,r.push([t,e])):r[n][1]=e,this},p.prototype.clear=function(){this.size=0,this.__data__={hash:new b,map:new(Se||h),string:new b}},p.prototype.delete=function(t){ -return t=Z(this,t).delete(t),this.size-=t?1:0,t},p.prototype.get=function(t){return Z(this,t).get(t)},p.prototype.has=function(t){return Z(this,t).has(t)},p.prototype.set=function(t,e){var r=Z(this,t),n=r.size;return r.set(t,e),this.size+=r.size==n?0:1,this},y.prototype.add=y.prototype.push=function(t){return this.__data__.set(t,"__lodash_hash_undefined__"),this},y.prototype.has=function(t){return this.__data__.has(t)},j.prototype.clear=function(){this.__data__=new h,this.size=0},j.prototype.delete=function(t){ -var e=this.__data__;return t=e.delete(t),this.size=e.size,t},j.prototype.get=function(t){return this.__data__.get(t)},j.prototype.has=function(t){return this.__data__.has(t)},j.prototype.set=function(t,e){var r=this.__data__;if(r instanceof h){var n=r.__data__;if(!Se||199>n.length)return n.push([t,e]),this.size=++r.size,this;r=this.__data__=new p(n)}return r.set(t,e),this.size=r.size,this};var Pe=function(t,e){return function(r,n){if(null==r)return r;if(!yt(r))return t(r,n);for(var o=r.length,c=e?o:-1,u=Object(r);(e?c--:++co?Ut:c,o=1),e=Object(e);++n ["/", "test", "\d+", undefined, "?"] - // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined] - '([\\/.])?(?:\\:(\\w+)(?:\\(((?:\\\\.|[^)])*)\\))?|\\(((?:\\\\.|[^)])*)\\))([+*?])?', - // Match regexp special characters that are always escaped. - '([.+*?=^!:${}()[\\]|\\/])' -].join('|'), 'g'); - -/** - * Escape the capturing group by escaping special characters and meaning. - * - * @param {String} group - * @return {String} - */ -function escapeGroup (group) { - return group.replace(/([=!:$\/()])/g, '\\$1'); -} - -/** - * Attach the keys as a property of the regexp. - * - * @param {RegExp} re - * @param {Array} keys - * @return {RegExp} - */ -function attachKeys (re, keys) { - re.keys = keys; - return re; -} - -/** - * Get the flags for a regexp from the options. - * - * @param {Object} options - * @return {String} - */ -function flags (options) { - return options.sensitive ? '' : 'i'; -} - -/** - * Pull out keys from a regexp. - * - * @param {RegExp} path - * @param {Array} keys - * @return {RegExp} - */ -function regexpToRegexp (path, keys) { - // Use a negative lookahead to match only capturing groups. - var groups = path.source.match(/\((?!\?)/g); - - if (groups) { - for (var i = 0; i < groups.length; i++) { - keys.push({ - name: i, - delimiter: null, - optional: false, - repeat: false - }); - } - } - - return attachKeys(path, keys); -} - -/** - * Transform an array into a regexp. - * - * @param {Array} path - * @param {Array} keys - * @param {Object} options - * @return {RegExp} - */ -function arrayToRegexp (path, keys, options) { - var parts = []; - - for (var i = 0; i < path.length; i++) { - parts.push(pathToRegexp(path[i], keys, options).source); - } - - var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)); - return attachKeys(regexp, keys); -} - -/** - * Replace the specific tags with regexp strings. - * - * @param {String} path - * @param {Array} keys - * @return {String} - */ -function replacePath (path, keys) { - var index = 0; - - function replace (_, escaped, prefix, key, capture, group, suffix, escape) { - if (escaped) { - return escaped; - } - - if (escape) { - return '\\' + escape; - } - - var repeat = suffix === '+' || suffix === '*'; - var optional = suffix === '?' || suffix === '*'; - - keys.push({ - name: key || index++, - delimiter: prefix || '/', - optional: optional, - repeat: repeat - }); - - prefix = prefix ? ('\\' + prefix) : ''; - capture = escapeGroup(capture || group || '[^' + (prefix || '\\/') + ']+?'); - - if (repeat) { - capture = capture + '(?:' + prefix + capture + ')*'; - } - - if (optional) { - return '(?:' + prefix + '(' + capture + '))?'; - } - - // Basic parameter support. - return prefix + '(' + capture + ')'; - } - - return path.replace(PATH_REGEXP, replace); -} - -/** - * Normalize the given path string, returning a regular expression. - * - * An empty array can be passed in for the keys, which will hold the - * placeholder key descriptions. For example, using `/user/:id`, `keys` will - * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. - * - * @param {(String|RegExp|Array)} path - * @param {Array} [keys] - * @param {Object} [options] - * @return {RegExp} - */ -function pathToRegexp (path, keys, options) { - keys = keys || []; - - if (!isArray(keys)) { - options = keys; - keys = []; - } else if (!options) { - options = {}; - } - - if (path instanceof RegExp) { - return regexpToRegexp(path, keys, options); - } - - if (isArray(path)) { - return arrayToRegexp(path, keys, options); - } - - var strict = options.strict; - var end = options.end !== false; - var route = replacePath(path, keys); - var endsWithSlash = path.charAt(path.length - 1) === '/'; - - // In non-strict mode we allow a slash at the end of match. If the path to - // match already ends with a slash, we remove it for consistency. The slash - // is valid at the end of a path match, not in the middle. This is important - // in non-ending mode, where "/test/" shouldn't match "/test//route". - if (!strict) { - route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?'; - } - - if (end) { - route += '$'; - } else { - // In non-ending mode, we need the capturing groups to match as much as - // possible by using a positive lookahead to the end or next path segment. - route += strict && endsWithSlash ? '' : '(?=\\/|$)'; - } - - return attachKeys(new RegExp('^' + route, flags(options)), keys); -} diff --git a/apidoc/vendor/polyfill.js b/apidoc/vendor/polyfill.js deleted file mode 100644 index 1d8c61cd..00000000 --- a/apidoc/vendor/polyfill.js +++ /dev/null @@ -1,96 +0,0 @@ -// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys -if (!Object.keys) { - Object.keys = (function () { - 'use strict'; - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; - - return function (obj) { - if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { - throw new TypeError('Object.keys called on non-object'); - } - - var result = [], prop, i; - - for (prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } - - if (hasDontEnumBug) { - for (i = 0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - return result; - }; - }()); -} - -//Production steps of ECMA-262, Edition 5, 15.4.4.18 -//Reference: http://es5.github.com/#x15.4.4.18 -if (!Array.prototype.forEach) { - Array.prototype.forEach = function (callback, thisArg) { - var T, k; - - if (this == null) { - throw new TypeError(' this is null or not defined'); - } - - // 1. Let O be the result of calling ToObject passing the |this| value as the argument. - var O = Object(this); - - // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". - // 3. Let len be ToUint32(lenValue). - var len = O.length >>> 0; - - // 4. If IsCallable(callback) is false, throw a TypeError exception. - // See: http://es5.github.com/#x9.11 - if (typeof callback !== "function") { - throw new TypeError(callback + " is not a function"); - } - - // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. - if (arguments.length > 1) { - T = thisArg; - } - - // 6. Let k be 0 - k = 0; - - // 7. Repeat, while k < len - while (k < len) { - var kValue; - - // a. Let Pk be ToString(k). - // This is implicit for LHS operands of the in operator - // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. - // This step can be combined with c - // c. If kPresent is true, then - if (k in O) { - // i. Let kValue be the result of calling the Get internal method of O with argument Pk. - kValue = O[k]; - - // ii. Call the Call internal method of callback with T as the this value and - // argument list containing kValue, k, and O. - callback.call(T, kValue, k, O); - } - // d. Increase k by 1. - k++; - } - // 8. return undefined - }; -} diff --git a/apidoc/vendor/prism.css b/apidoc/vendor/prism.css deleted file mode 100644 index 4fac8142..00000000 --- a/apidoc/vendor/prism.css +++ /dev/null @@ -1,125 +0,0 @@ -/* PrismJS 1.21.0 -https://prismjs.com/download.html#themes=prism-tomorrow&languages=clike+javascript+bash+c+csharp+cpp+clojure+elixir+erlang+go+http+json+json5+jsonp+lua+perl+python+rust */ -/** - * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML - * Based on https://github.com/chriskempson/tomorrow-theme - * @author Rose Pritchard - */ - -code[class*="language-"], -pre[class*="language-"] { - color: #ccc; - background: none; - font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - font-size: 1em; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - line-height: 1.5; - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; - -} - -/* Code blocks */ -pre[class*="language-"] { - padding: 1em; - margin: .5em 0; - overflow: auto; -} - -:not(pre) > code[class*="language-"], -pre[class*="language-"] { - background: #2d2d2d; -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - padding: .1em; - border-radius: .3em; - white-space: normal; -} - -.token.comment, -.token.block-comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: #999; -} - -.token.punctuation { - color: #ccc; -} - -.token.tag, -.token.attr-name, -.token.namespace, -.token.deleted { - color: #e2777a; -} - -.token.function-name { - color: #6196cc; -} - -.token.boolean, -.token.number, -.token.function { - color: #f08d49; -} - -.token.property, -.token.class-name, -.token.constant, -.token.symbol { - color: #f8c555; -} - -.token.selector, -.token.important, -.token.atrule, -.token.keyword, -.token.builtin { - color: #cc99cd; -} - -.token.string, -.token.char, -.token.attr-value, -.token.regex, -.token.variable { - color: #7ec699; -} - -.token.operator, -.token.entity, -.token.url { - color: #67cdcc; -} - -.token.important, -.token.bold { - font-weight: bold; -} -.token.italic { - font-style: italic; -} - -.token.entity { - cursor: help; -} - -.token.inserted { - color: green; -} - diff --git a/apidoc/vendor/prism.js b/apidoc/vendor/prism.js deleted file mode 100644 index 137746f1..00000000 --- a/apidoc/vendor/prism.js +++ /dev/null @@ -1,21 +0,0 @@ -/* PrismJS 1.21.0 -https://prismjs.com/download.html#themes=prism-tomorrow&languages=clike+javascript+bash+c+csharp+cpp+clojure+elixir+erlang+go+http+json+json5+jsonp+lua+perl+python+rust */ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);k+=y.value.length,y=y.next){var b=y.value;if(t.length>n.length)return;if(!(b instanceof W)){var x=1;if(h&&y!=t.tail.prev){m.lastIndex=k;var w=m.exec(n);if(!w)break;var A=w.index+(f&&w[1]?w[1].length:0),P=w.index+w[0].length,S=k;for(S+=y.value.length;S<=A;)y=y.next,S+=y.value.length;if(S-=y.value.length,k=S,y.value instanceof W)continue;for(var E=y;E!==t.tail&&(Sl.reach&&(l.reach=j);var C=y.prev;L&&(C=I(t,C,L),k+=L.length),z(t,C,x);var _=new W(o,g?M.tokenize(O,g):O,v,O);y=I(t,C,_),N&&I(t,y,N),1"+a.content+""},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(M.highlight(r,M.languages[t],t)),a&&u.close()},!1)),M;var e=M.util.currentScript();function t(){M.manual||M.highlightAll()}if(e&&(M.filename=e.src,e.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var r=document.readyState;"loading"===r||"interactive"===r&&e&&e.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)}return M}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); -Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; -Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|(?:get|set)(?=\s*[\[$\w\xA0-\uFFFF])|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.js=Prism.languages.javascript; -!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)\w+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b\w+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+?)\s*(?:\r?\n|\r)[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s*(?:\r?\n|\r)[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\](?:\\\\)*)(["'])(?:\\[\s\S]|\$\([^)]+\)|`[^`]+`|(?!\2)[^\\])*\2/,lookbehind:!0,greedy:!0,inside:n}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|aptitude|apt-cache|apt-get|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:if|then|else|elif|fi|for|while|in|case|esac|function|select|do|done|until)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|break|cd|continue|eval|exec|exit|export|getopts|hash|pwd|readonly|return|shift|test|times|trap|umask|unset|alias|bind|builtin|caller|command|declare|echo|enable|help|let|local|logout|mapfile|printf|read|readarray|source|type|typeset|ulimit|unalias|set|shopt)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:true|false)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|==?|!=?|=~|<<[<-]?|[&\d]?>>|\d?[<>]&?|&[>&]?|\|[&|]?|<=?|>=?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}};for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],r=n.variable[1].inside,s=0;s>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/,number:/(?:\b0x(?:[\da-f]+\.?[\da-f]*|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+(?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],comment:Prism.languages.c.comment,directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}},constant:/\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/}),delete Prism.languages.c.boolean; -!function(s){function a(e,s){return e.replace(/<<(\d+)>>/g,function(e,n){return"(?:"+s[+n]+")"})}function t(e,n,s){return RegExp(a(e,n),s||"")}function e(e,n){for(var s=0;s>/g,function(){return"(?:"+e+")"});return e.replace(/<>/g,"[^\\s\\S]")}var n="bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void",r="class enum interface struct",i="add alias and ascending async await by descending from get global group into join let nameof not notnull on or orderby partial remove select set unmanaged value when where where",o="abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield";function l(e){return"\\b(?:"+e.trim().replace(/ /g,"|")+")\\b"}var d=l(r),p=RegExp(l(n+" "+r+" "+i+" "+o)),c=l(r+" "+i+" "+o),u=l(n+" "+r+" "+o),g=e("<(?:[^<>;=+\\-*/%&|^]|<>)*>",2),b=e("\\((?:[^()]|<>)*\\)",2),h="@?\\b[A-Za-z_]\\w*\\b",f=a("<<0>>(?:\\s*<<1>>)?",[h,g]),m=a("(?!<<0>>)<<1>>(?:\\s*\\.\\s*<<1>>)*",[c,f]),k="\\[\\s*(?:,\\s*)*\\]",y=a("<<0>>(?:\\s*(?:\\?\\s*)?<<1>>)*(?:\\s*\\?)?",[m,k]),w=a("(?:<<0>>|<<1>>)(?:\\s*(?:\\?\\s*)?<<2>>)*(?:\\s*\\?)?",[a("\\(<<0>>+(?:,<<0>>+)+\\)",[a("[^,()<>[\\];=+\\-*/%&|^]|<<0>>|<<1>>|<<2>>",[g,b,k])]),m,k]),v={keyword:p,punctuation:/[<>()?,.:[\]]/},x="'(?:[^\r\n'\\\\]|\\\\.|\\\\[Uux][\\da-fA-F]{1,8})'",$='"(?:\\\\.|[^\\\\"\r\n])*"';s.languages.csharp=s.languages.extend("clike",{string:[{pattern:t("(^|[^$\\\\])<<0>>",['@"(?:""|\\\\[^]|[^\\\\"])*"(?!")']),lookbehind:!0,greedy:!0},{pattern:t("(^|[^@$\\\\])<<0>>",[$]),lookbehind:!0,greedy:!0},{pattern:RegExp(x),greedy:!0,alias:"character"}],"class-name":[{pattern:t("(\\busing\\s+static\\s+)<<0>>(?=\\s*;)",[m]),lookbehind:!0,inside:v},{pattern:t("(\\busing\\s+<<0>>\\s*=\\s*)<<1>>(?=\\s*;)",[h,w]),lookbehind:!0,inside:v},{pattern:t("(\\busing\\s+)<<0>>(?=\\s*=)",[h]),lookbehind:!0},{pattern:t("(\\b<<0>>\\s+)<<1>>",[d,f]),lookbehind:!0,inside:v},{pattern:t("(\\bcatch\\s*\\(\\s*)<<0>>",[m]),lookbehind:!0,inside:v},{pattern:t("(\\bwhere\\s+)<<0>>",[h]),lookbehind:!0},{pattern:t("(\\b(?:is(?:\\s+not)?|as)\\s+)<<0>>",[y]),lookbehind:!0,inside:v},{pattern:t("\\b<<0>>(?=\\s+(?!<<1>>)<<2>>(?:\\s*[=,;:{)\\]]|\\s+(?:in|when)\\b))",[w,u,h]),inside:v}],keyword:p,number:/(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:ul|lu|[dflmu])?\b/i,operator:/>>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,punctuation:/\?\.?|::|[{}[\];(),.:]/}),s.languages.insertBefore("csharp","number",{range:{pattern:/\.\./,alias:"operator"}}),s.languages.insertBefore("csharp","punctuation",{"named-parameter":{pattern:t("([(,]\\s*)<<0>>(?=\\s*:)",[h]),lookbehind:!0,alias:"punctuation"}}),s.languages.insertBefore("csharp","class-name",{namespace:{pattern:t("(\\b(?:namespace|using)\\s+)<<0>>(?:\\s*\\.\\s*<<0>>)*(?=\\s*[;{])",[h]),lookbehind:!0,inside:{punctuation:/\./}},"type-expression":{pattern:t("(\\b(?:default|typeof|sizeof)\\s*\\(\\s*)(?:[^()\\s]|\\s(?!\\s*\\))|<<0>>)*(?=\\s*\\))",[b]),lookbehind:!0,alias:"class-name",inside:v},"return-type":{pattern:t("<<0>>(?=\\s+(?:<<1>>\\s*(?:=>|[({]|\\.\\s*this\\s*\\[)|this\\s*\\[))",[w,m]),inside:v,alias:"class-name"},"constructor-invocation":{pattern:t("(\\bnew\\s+)<<0>>(?=\\s*[[({])",[w]),lookbehind:!0,inside:v,alias:"class-name"},"generic-method":{pattern:t("<<0>>\\s*<<1>>(?=\\s*\\()",[h,g]),inside:{function:t("^<<0>>",[h]),generic:{pattern:RegExp(g),alias:"class-name",inside:v}}},"type-list":{pattern:t("\\b((?:<<0>>\\s+<<1>>|where\\s+<<2>>)\\s*:\\s*)(?:<<3>>|<<4>>)(?:\\s*,\\s*(?:<<3>>|<<4>>))*(?=\\s*(?:where|[{;]|=>|$))",[d,f,h,w,p.source]),lookbehind:!0,inside:{keyword:p,"class-name":{pattern:RegExp(w),greedy:!0,inside:v},punctuation:/,/}},preprocessor:{pattern:/(^\s*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(\s*#)\b(?:define|elif|else|endif|endregion|error|if|line|pragma|region|undef|warning)\b/,lookbehind:!0,alias:"keyword"}}}});var _=$+"|"+x,B=a("/(?![*/])|//[^\r\n]*[\r\n]|/\\*(?:[^*]|\\*(?!/))*\\*/|<<0>>",[_]),E=e(a("[^\"'/()]|<<0>>|\\(<>*\\)",[B]),2),R="\\b(?:assembly|event|field|method|module|param|property|return|type)\\b",P=a("<<0>>(?:\\s*\\(<<1>>*\\))?",[m,E]);s.languages.insertBefore("csharp","class-name",{attribute:{pattern:t("((?:^|[^\\s\\w>)?])\\s*\\[\\s*)(?:<<0>>\\s*:\\s*)?<<1>>(?:\\s*,\\s*<<1>>)*(?=\\s*\\])",[R,P]),lookbehind:!0,greedy:!0,inside:{target:{pattern:t("^<<0>>(?=\\s*:)",[R]),alias:"keyword"},"attribute-arguments":{pattern:t("\\(<<0>>*\\)",[E]),inside:s.languages.csharp},"class-name":{pattern:RegExp(m),inside:{punctuation:/\./}},punctuation:/[:,]/}}});var z=":[^}\r\n]+",S=e(a("[^\"'/()]|<<0>>|\\(<>*\\)",[B]),2),j=a("\\{(?!\\{)(?:(?![}:])<<0>>)*<<1>>?\\}",[S,z]),A=e(a("[^\"'/()]|/(?!\\*)|/\\*(?:[^*]|\\*(?!/))*\\*/|<<0>>|\\(<>*\\)",[_]),2),F=a("\\{(?!\\{)(?:(?![}:])<<0>>)*<<1>>?\\}",[A,z]);function U(e,n){return{interpolation:{pattern:t("((?:^|[^{])(?:\\{\\{)*)<<0>>",[e]),lookbehind:!0,inside:{"format-string":{pattern:t("(^\\{(?:(?![}:])<<0>>)*)<<1>>(?=\\}$)",[n,z]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-csharp",inside:s.languages.csharp}}},string:/[\s\S]+/}}s.languages.insertBefore("csharp","string",{"interpolation-string":[{pattern:t('(^|[^\\\\])(?:\\$@|@\\$)"(?:""|\\\\[^]|\\{\\{|<<0>>|[^\\\\{"])*"',[j]),lookbehind:!0,greedy:!0,inside:U(j,S)},{pattern:t('(^|[^@\\\\])\\$"(?:\\\\.|\\{\\{|<<0>>|[^\\\\"{])*"',[F]),lookbehind:!0,greedy:!0,inside:U(F,A)}]})}(Prism),Prism.languages.dotnet=Prism.languages.cs=Prism.languages.csharp; -!function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char8_t|char16_t|char32_t|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|co_await|co_return|co_yield|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/;e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp("(\\b(?:class|concept|enum|struct|typename)\\s+)(?!)\\w+".replace(//g,function(){return t.source}),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+\.?[\da-f']*|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+\.?[\d']*|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]*/i,greedy:!0},operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:true|false)\b/}),e.languages.insertBefore("cpp","string",{"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)(?:[^;{}"'])+?(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","operator",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(Prism); -Prism.languages.clojure={comment:/;.*/,string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0},operator:/(?:::|[:|'])\b[a-z][\w*+!?-]*\b/i,keyword:{pattern:/([^\w+*'?-])(?:def|if|do|let|\.\.|quote|var|->>|->|fn|loop|recur|throw|try|monitor-enter|\.|new|set!|def\-|defn|defn\-|defmacro|defmulti|defmethod|defstruct|defonce|declare|definline|definterface|defprotocol|==|defrecord|>=|deftype|<=|defproject|ns|\*|\+|\-|\/|<|=|>|accessor|agent|agent-errors|aget|alength|all-ns|alter|and|append-child|apply|array-map|aset|aset-boolean|aset-byte|aset-char|aset-double|aset-float|aset-int|aset-long|aset-short|assert|assoc|await|await-for|bean|binding|bit-and|bit-not|bit-or|bit-shift-left|bit-shift-right|bit-xor|boolean|branch\?|butlast|byte|cast|char|children|class|clear-agent-errors|comment|commute|comp|comparator|complement|concat|conj|cons|constantly|cond|if-not|construct-proxy|contains\?|count|create-ns|create-struct|cycle|dec|deref|difference|disj|dissoc|distinct|doall|doc|dorun|doseq|dosync|dotimes|doto|double|down|drop|drop-while|edit|end\?|ensure|eval|every\?|false\?|ffirst|file-seq|filter|find|find-doc|find-ns|find-var|first|float|flush|for|fnseq|frest|gensym|get-proxy-class|get|hash-map|hash-set|identical\?|identity|if-let|import|in-ns|inc|index|insert-child|insert-left|insert-right|inspect-table|inspect-tree|instance\?|int|interleave|intersection|into|into-array|iterate|join|key|keys|keyword|keyword\?|last|lazy-cat|lazy-cons|left|lefts|line-seq|list\*|list|load|load-file|locking|long|loop|macroexpand|macroexpand-1|make-array|make-node|map|map-invert|map\?|mapcat|max|max-key|memfn|merge|merge-with|meta|min|min-key|name|namespace|neg\?|new|newline|next|nil\?|node|not|not-any\?|not-every\?|not=|ns-imports|ns-interns|ns-map|ns-name|ns-publics|ns-refers|ns-resolve|ns-unmap|nth|nthrest|or|parse|partial|path|peek|pop|pos\?|pr|pr-str|print|print-str|println|println-str|prn|prn-str|project|proxy|proxy-mappings|quot|rand|rand-int|range|re-find|re-groups|re-matcher|re-matches|re-pattern|re-seq|read|read-line|reduce|ref|ref-set|refer|rem|remove|remove-method|remove-ns|rename|rename-keys|repeat|replace|replicate|resolve|rest|resultset-seq|reverse|rfirst|right|rights|root|rrest|rseq|second|select|select-keys|send|send-off|seq|seq-zip|seq\?|set|short|slurp|some|sort|sort-by|sorted-map|sorted-map-by|sorted-set|special-symbol\?|split-at|split-with|str|string\?|struct|struct-map|subs|subvec|symbol|symbol\?|sync|take|take-nth|take-while|test|time|to-array|to-array-2d|tree-seq|true\?|union|up|update-proxy|val|vals|var-get|var-set|var\?|vector|vector-zip|vector\?|when|when-first|when-let|when-not|with-local-vars|with-meta|with-open|with-out-str|xml-seq|xml-zip|zero\?|zipmap|zipper)(?=[^\w+*'?-])/,lookbehind:!0},boolean:/\b(?:true|false|nil)\b/,number:/\b[\da-f]+\b/i,punctuation:/[{}\[\](),]/}; -Prism.languages.elixir={comment:/#.*/m,regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},"attr-name":/\w+\??:(?!:)/,capture:{pattern:/(^|[^&])&(?:[^&\s\d()][^\s()]*|(?=\())/,lookbehind:!0,alias:"function"},argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,boolean:/\b(?:true|false|nil)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.elixir.string.forEach(function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.languages.elixir}}}}); -Prism.languages.erlang={comment:/%.+/,string:{pattern:/"(?:\\.|[^\\"\r\n])*"/,greedy:!0},"quoted-function":{pattern:/'(?:\\.|[^\\'\r\n])+'(?=\()/,alias:"function"},"quoted-atom":{pattern:/'(?:\\.|[^\\'\r\n])+'/,alias:"atom"},boolean:/\b(?:true|false)\b/,keyword:/\b(?:fun|when|case|of|end|if|receive|after|try|catch)\b/,number:[/\$\\?./,/\d+#[a-z0-9]+/i,/(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i],function:/\b[a-z][\w@]*(?=\()/,variable:{pattern:/(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,lookbehind:!0},operator:[/[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:bnot|div|rem|band|bor|bxor|bsl|bsr|not|and|or|xor|orelse|andalso)\b/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],atom:/\b[a-z][\w@]*/,punctuation:/[()[\]{}:;,.#|]|<<|>>/}; -Prism.languages.go=Prism.languages.extend("clike",{keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,builtin:/\b(?:bool|byte|complex(?:64|128)|error|float(?:32|64)|rune|string|u?int(?:8|16|32|64)?|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(?:ln)?|real|recover)\b/,boolean:/\b(?:_|iota|nil|true|false)\b/,operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,number:/(?:\b0x[a-f\d]+|(?:\b\d+\.?\d*|\B\.\d+)(?:e[-+]?\d+)?)i?/i,string:{pattern:/(["'`])(?:\\[\s\S]|(?!\1)[^\\])*\1/,greedy:!0}}),delete Prism.languages.go["class-name"]; -!function(t){t.languages.http={"request-line":{pattern:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\s(?:https?:\/\/|\/)\S+\sHTTP\/[0-9.]+/m,inside:{property:/^(?:POST|GET|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT)\b/,"attr-name":/:\w+/}},"response-status":{pattern:/^HTTP\/1.[01] \d+.*/m,inside:{property:{pattern:/(^HTTP\/1.[01] )\d+.*/i,lookbehind:!0}}},"header-name":{pattern:/^[\w-]+:(?=.)/m,alias:"keyword"}};var a,e,n,i=t.languages,p={"application/javascript":i.javascript,"application/json":i.json||i.javascript,"application/xml":i.xml,"text/xml":i.xml,"text/html":i.html,"text/css":i.css},s={"application/json":!0,"application/xml":!0};for(var r in p)if(p[r]){a=a||{};var T=s[r]?(void 0,n=(e=r).replace(/^[a-z]+\//,""),"(?:"+e+"|\\w+/(?:[\\w.-]+\\+)+"+n+"(?![+\\w.-]))"):r;a[r.replace(/\//g,"-")]={pattern:RegExp("(content-type:\\s*"+T+"[\\s\\S]*?)(?:\\r?\\n|\\r){2}[\\s\\S]*","i"),lookbehind:!0,inside:p[r]}}a&&t.languages.insertBefore("http","header-name",a)}(Prism); -Prism.languages.json={property:{pattern:/"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,greedy:!0},string:{pattern:/"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:true|false)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json; -!function(n){var e=/("|')(?:\\(?:\r\n?|\n|.)|(?!\1)[^\\\r\n])*\1/;n.languages.json5=n.languages.extend("json",{property:[{pattern:RegExp(e.source+"(?=\\s*:)"),greedy:!0},{pattern:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*:)/,alias:"unquoted"}],string:{pattern:e,greedy:!0},number:/[+-]?\b(?:NaN|Infinity|0x[a-fA-F\d]+)\b|[+-]?(?:\b\d+\.?\d*|\B\.\d+)(?:[eE][+-]?\d+\b)?/})}(Prism); -Prism.languages.jsonp=Prism.languages.extend("json",{punctuation:/[{}[\]();,.]/}),Prism.languages.insertBefore("jsonp","punctuation",{function:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/}); -Prism.languages.lua={comment:/^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,string:{pattern:/(["'])(?:(?!\1)[^\\\r\n]|\\z(?:\r\n|\s)|\\(?:\r\n|[\s\S]))*\1|\[(=*)\[[\s\S]*?\]\2\]/,greedy:!0},number:/\b0x[a-f\d]+\.?[a-f\d]*(?:p[+-]?\d+)?\b|\b\d+(?:\.\B|\.?\d*(?:e[+-]?\d+)?\b)|\B\.\d+(?:e[+-]?\d+)?\b/i,keyword:/\b(?:and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,function:/(?!\d)\w+(?=\s*(?:[({]))/,operator:[/[-+*%^&|#]|\/\/?|<[<=]?|>[>=]?|[=~]=?/,{pattern:/(^|[^.])\.\.(?!\.)/,lookbehind:!0}],punctuation:/[\[\](){},;]|\.+|:+/}; -Prism.languages.perl={comment:[{pattern:/(^\s*)=\w+[\s\S]*?=cut.*/m,lookbehind:!0},{pattern:/(^|[^\\$])#.*/,lookbehind:!0}],string:[{pattern:/\b(?:q|qq|qx|qw)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\((?:[^()\\]|\\[\s\S])*\)/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\{(?:[^{}\\]|\\[\s\S])*\}/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\[(?:[^[\]\\]|\\[\s\S])*\]/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*<(?:[^<>\\]|\\[\s\S])*>/,greedy:!0},{pattern:/("|`)(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/'(?:[^'\\\r\n]|\\.)*'/,greedy:!0}],regex:[{pattern:/\b(?:m|qr)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngc]*/,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s+([a-zA-Z0-9])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\((?:[^()\\]|\\[\s\S])*\)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\{(?:[^{}\\]|\\[\s\S])*\}\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\[(?:[^[\]\\]|\\[\s\S])*\]\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*<(?:[^<>\\]|\\[\s\S])*>\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/\/(?:[^\/\\\r\n]|\\.)*\/[msixpodualngc]*(?=\s*(?:$|[\r\n,.;})&|\-+*~<>!?^]|(?:lt|gt|le|ge|eq|ne|cmp|not|and|or|xor|x)\b))/,greedy:!0}],variable:[/[&*$@%]\{\^[A-Z]+\}/,/[&*$@%]\^[A-Z_]/,/[&*$@%]#?(?=\{)/,/[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+)+(?:::)*/i,/[&*$@%]\d+/,/(?!%=)[$@%][!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/],filehandle:{pattern:/<(?![<=])\S*>|\b_\b/,alias:"symbol"},vstring:{pattern:/v\d+(?:\.\d+)*|\d+(?:\.\d+){2,}/,alias:"string"},function:{pattern:/sub [a-z0-9_]+/i,inside:{keyword:/sub/}},keyword:/\b(?:any|break|continue|default|delete|die|do|else|elsif|eval|for|foreach|given|goto|if|last|local|my|next|our|package|print|redo|require|return|say|state|sub|switch|undef|unless|until|use|when|while)\b/,number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0b[01](?:_?[01])*|(?:\d(?:_?\d)*)?\.?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)\b/,operator:/-[rwxoRWXOezsfdlpSbctugkTBMAC]\b|\+[+=]?|-[-=>]?|\*\*?=?|\/\/?=?|=[=~>]?|~[~=]?|\|\|?=?|&&?=?|<(?:=>?|<=?)?|>>?=?|![~=]?|[%^]=?|\.(?:=|\.\.?)?|[\\?]|\bx(?:=|\b)|\b(?:lt|gt|le|ge|eq|ne|cmp|not|and|or|xor)\b/,punctuation:/[{}[\];(),:]/}; -Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},"string-interpolation":{pattern:/(?:f|rf|fr)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|rb|br)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^\s*)@\w+(?:\.\w+)*/im,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:True|False|None)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python; -!function(e){for(var a="/\\*(?:[^*/]|\\*(?!/)|/(?!\\*)|)*\\*/",t=0;t<2;t++)a=a.replace(//g,function(){return a});a=a.replace(//g,function(){return"[^\\s\\S]"}),e.languages.rust={comment:[{pattern:RegExp("(^|[^\\\\])"+a),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u{(?:[\da-fA-F]_*){1,6}|.)|[^\\\r\n\t'])'/,greedy:!0,alias:"string"},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|Self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:[ui](?:8|16|32|64|128|size)|f(?:32|64)|bool|char|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:\d(?:_?\d)*)?\.?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:[iu](?:8|16|32|64|size)?|f32|f64))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(Prism); diff --git a/apidoc/vendor/require.min.js b/apidoc/vendor/require.min.js deleted file mode 100644 index de7c7d2f..00000000 --- a/apidoc/vendor/require.min.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - RequireJS 2.1.22 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved. - Available via the MIT or new BSD license. - see: http://github.com/jrburke/requirejs for details -*/ -var requirejs,require,define; -(function(ha){function L(b){return"[object Function]"===R.call(b)}function M(b){return"[object Array]"===R.call(b)}function x(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(L(l)){try{f=h.execCb(c,l,b,f)}catch(d){a=d}this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports: -this.usingExports&&(f=this.exports));if(a){if(this.events.error&&this.map.isDefine||k.onError!==ia)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",B(this.error=a);if("undefined"!==typeof console&&console.error)console.error(a);else k.onError(a)}}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,k.onResourceLoad)){var e=[];x(this.depMaps,function(a){e.push(a.normalizedMap||a)});k.onResourceLoad(h, -this.map,e)}D(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}},callPlugin:function(){var a=this.map,b=a.id,d=q(a.prefix);this.depMaps.push(d);v(d,"defined",y(this,function(f){var l,d,e=g(ga,this.map.id),N=this.map.name,p=this.map.parentMap?this.map.parentMap.name:null,r=h.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(N=f.normalize(N,function(a){return c(a, -p,!0)})||""),d=q(a.prefix+"!"+N,this.map.parentMap),v(d,"defined",y(this,function(a){this.map.normalizedMap=d;this.init([],function(){return a},null,{enabled:!0,ignore:!0})}),f=g(t,d.id)){this.depMaps.push(d);if(this.events.error)f.on("error",y(this,function(a){this.emit("error",a)});f.enable()}}else e?(this.map.url=h.nameToUrl(e),this.load()):(l=y(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=y(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b]; -E(t,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&D(a.map.id)});B(a)}),l.fromText=y(this,function(f,c){var d=a.name,e=q(d),N=T;c&&(f=c);N&&(T=!1);u(e);w(m.config,b)&&(m.config[d]=m.config[b]);try{k.exec(f)}catch(g){return B(G("fromtexteval","fromText eval for "+b+" failed: "+g,g,[b]))}N&&(T=!0);this.depMaps.push(e);h.completeLoad(d);r([d],l)}),f.load(a.name,r,l,m))});h.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){aa[this.map.id]=this;this.enabling=this.enabled=!0;x(this.depMaps, -y(this,function(a,b){var c,f;if("string"===typeof a){a=q(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=g(S,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;v(a,"defined",y(this,function(a){this.undefed||(this.defineDep(b,a),this.check())});this.errback?v(a,"error",y(this,this.errback)):this.events.error&&v(a,"error",y(this,function(a){this.emit("error",a)})}c=a.id;f=t[c];w(S,c)||!f||f.enabled||h.enable(a,this)});E(this.pluginMaps,y(this,function(a){var b= -g(t,a.id);b&&!b.enabled&&h.enable(a,this)});this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){x(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};h={config:m,contextName:b,registry:t,defined:r,urlFetched:X,defQueue:H,defQueueMap:{},Module:ea,makeModuleMap:q,nextTick:k.nextTick,onError:B,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=m.shim,c={paths:!0, -bundles:!0,config:!0,map:!0};E(a,function(a,b){c[b]?(m[b]||(m[b]={}),Z(m[b],a,!0,!0)):m[b]=a});a.bundles&&E(a.bundles,function(a,b){x(a,function(a){a!==b&&(ga[a]=b)})});a.shim&&(E(a.shim,function(a,c){M(a)&&(a={deps:a});!a.exports&&!a.init||a.exportsFn||(a.exportsFn=h.makeShimExports(a));b[c]=a}),m.shim=b);a.packages&&x(a.packages,function(a){var b;a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(m.paths[b]=a.location);m.pkgs[b]=a.name+"/"+(a.main||"main").replace(na,"").replace(V,"")});E(t, -function(a,b){a.inited||a.map.unnormalized||(a.map=q(b,null,!0))});(a.deps||a.callback)&&h.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ha,arguments));return b||a.exports&&ja(a.exports)}},makeRequire:function(a,n){function e(c,d,g){var m,p;n.enableBuildCallback&&d&&L(d)&&(d.__requireJsBuild=!0);if("string"===typeof c){if(L(d))return B(G("requireargs","Invalid require call"),g);if(a&&w(S,c))return S[c](t[a.id]);if(k.get)return k.get(h, -c,a,e);m=q(c,a,!1,!0);m=m.id;return w(r,m)?r[m]:B(G("notloaded",'Module name "'+m+'" has not been loaded yet for context: '+b+(a?"":". Use require([])")))}Q();h.nextTick(function(){Q();p=u(q(null,a));p.skipMap=n.skipMap;p.init(c,d,g,{enabled:!0});I()});return e}n=n||{};Z(e,{isBrowser:F,toUrl:function(b){var d,e=b.lastIndexOf("."),n=b.split("/")[0];-1!==e&&("."!==n&&".."!==n||1e.attachEvent.toString().indexOf("[native code")||da?(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)):(T=!0,e.attachEvent("onreadystatechange",b.onScriptLoad));e.src=d;Q=e;I?D.insertBefore(e,I):D.appendChild(e);Q=null;return e}if(ka)try{importScripts(d),b.completeLoad(c)}catch(q){b.onError(G("importscripts","importScripts failed for "+ -c+" at "+d,q,[c]))}};F&&!v.skipDataMain&&Y(document.getElementsByTagName("script"),function(b){D||(D=b.parentNode);if(P=b.getAttribute("data-main"))return u=P,v.baseUrl||(J=u.split("/"),u=J.pop(),U=J.length?J.join("/")+"/":"./",v.baseUrl=U),u=u.replace(V,""),k.jsExtRegExp.test(u)&&(u=P),v.deps=v.deps?v.deps.concat(u):[u],!0});define=function(b,c,d){var g,e;"string"!==typeof b&&(d=c,c=b,b=null);M(c)||(d=c,c=null);!c&&L(d)&&(c=[],d.length&&(d.toString().replace(qa,"").replace(ra,function(b,d){c.push(d)}), -c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));T&&(g=Q||pa())&&(b||(b=g.getAttribute("data-requiremodule")),e=K[g.getAttribute("data-requirecontext")]);e?(e.defQueue.push([b,c,d]),e.defQueueMap[b]=!0):W.push([b,c,d])};define.amd={jQuery:!0};k.exec=function(b){return eval(b)};k(v)}})(this); diff --git a/apidoc/vendor/semver.min.js b/apidoc/vendor/semver.min.js deleted file mode 100644 index c2b3ff48..00000000 --- a/apidoc/vendor/semver.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(e){if(typeof module==="object"&&module.exports===e)e=module.exports=K;e.SEMVER_SPEC_VERSION="2.0.0";var r=256;var t=Number.MAX_SAFE_INTEGER||9007199254740991;var n=e.re=[];var i=e.src=[];var s=0;var o=s++;i[o]="0|[1-9]\\d*";var a=s++;i[a]="[0-9]+";var f=s++;i[f]="\\d*[a-zA-Z-][a-zA-Z0-9-]*";var u=s++;i[u]="("+i[o]+")\\."+"("+i[o]+")\\."+"("+i[o]+")";var l=s++;i[l]="("+i[a]+")\\."+"("+i[a]+")\\."+"("+i[a]+")";var p=s++;i[p]="(?:"+i[o]+"|"+i[f]+")";var h=s++;i[h]="(?:"+i[a]+"|"+i[f]+")";var c=s++;i[c]="(?:-("+i[p]+"(?:\\."+i[p]+")*))";var v=s++;i[v]="(?:-?("+i[h]+"(?:\\."+i[h]+")*))";var m=s++;i[m]="[0-9A-Za-z-]+";var g=s++;i[g]="(?:\\+("+i[m]+"(?:\\."+i[m]+")*))";var w=s++;var y="v?"+i[u]+i[c]+"?"+i[g]+"?";i[w]="^"+y+"$";var d="[v=\\s]*"+i[l]+i[v]+"?"+i[g]+"?";var j=s++;i[j]="^"+d+"$";var b=s++;i[b]="((?:<|>)?=?)";var E=s++;i[E]=i[a]+"|x|X|\\*";var $=s++;i[$]=i[o]+"|x|X|\\*";var k=s++;i[k]="[v=\\s]*("+i[$]+")"+"(?:\\.("+i[$]+")"+"(?:\\.("+i[$]+")"+"(?:"+i[c]+")?"+i[g]+"?"+")?)?";var R=s++;i[R]="[v=\\s]*("+i[E]+")"+"(?:\\.("+i[E]+")"+"(?:\\.("+i[E]+")"+"(?:"+i[v]+")?"+i[g]+"?"+")?)?";var S=s++;i[S]="^"+i[b]+"\\s*"+i[k]+"$";var x=s++;i[x]="^"+i[b]+"\\s*"+i[R]+"$";var I=s++;i[I]="(?:~>?)";var T=s++;i[T]="(\\s*)"+i[I]+"\\s+";n[T]=new RegExp(i[T],"g");var V="$1~";var A=s++;i[A]="^"+i[I]+i[k]+"$";var C=s++;i[C]="^"+i[I]+i[R]+"$";var M=s++;i[M]="(?:\\^)";var N=s++;i[N]="(\\s*)"+i[M]+"\\s+";n[N]=new RegExp(i[N],"g");var _="$1^";var z=s++;i[z]="^"+i[M]+i[k]+"$";var P=s++;i[P]="^"+i[M]+i[R]+"$";var X=s++;i[X]="^"+i[b]+"\\s*("+d+")$|^$";var Z=s++;i[Z]="^"+i[b]+"\\s*("+y+")$|^$";var q=s++;i[q]="(\\s*)"+i[b]+"\\s*("+d+"|"+i[k]+")";n[q]=new RegExp(i[q],"g");var L="$1$2$3";var F=s++;i[F]="^\\s*("+i[k]+")"+"\\s+-\\s+"+"("+i[k]+")"+"\\s*$";var G=s++;i[G]="^\\s*("+i[R]+")"+"\\s+-\\s+"+"("+i[R]+")"+"\\s*$";var O=s++;i[O]="(<|>)?=?\\s*\\*";for(var B=0;Br)return null;var i=t?n[j]:n[w];if(!i.test(e))return null;try{return new K(e,t)}catch(s){return null}}e.valid=H;function H(e,r){var t=D(e,r);return t?t.version:null}e.clean=J;function J(e,r){var t=D(e.trim().replace(/^[=v]+/,""),r);return t?t.version:null}e.SemVer=K;function K(e,i){if(e instanceof K){if(e.loose===i)return e;else e=e.version}else if(typeof e!=="string"){throw new TypeError("Invalid Version: "+e)}if(e.length>r)throw new TypeError("version is longer than "+r+" characters");if(!(this instanceof K))return new K(e,i);this.loose=i;var s=e.trim().match(i?n[j]:n[w]);if(!s)throw new TypeError("Invalid Version: "+e);this.raw=e;this.major=+s[1];this.minor=+s[2];this.patch=+s[3];if(this.major>t||this.major<0)throw new TypeError("Invalid major version");if(this.minor>t||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>t||this.patch<0)throw new TypeError("Invalid patch version");if(!s[4])this.prerelease=[];else this.prerelease=s[4].split(".").map(function(e){if(/^[0-9]+$/.test(e)){var r=+e;if(r>=0&&r'};K.prototype.toString=function(){return this.version};K.prototype.compare=function(e){if(!(e instanceof K))e=new K(e,this.loose);return this.compareMain(e)||this.comparePre(e)};K.prototype.compareMain=function(e){if(!(e instanceof K))e=new K(e,this.loose);return Y(this.major,e.major)||Y(this.minor,e.minor)||Y(this.patch,e.patch)};K.prototype.comparePre=function(e){if(!(e instanceof K))e=new K(e,this.loose);if(this.prerelease.length&&!e.prerelease.length)return-1;else if(!this.prerelease.length&&e.prerelease.length)return 1;else if(!this.prerelease.length&&!e.prerelease.length)return 0;var r=0;do{var t=this.prerelease[r];var n=e.prerelease[r];if(t===undefined&&n===undefined)return 0;else if(n===undefined)return 1;else if(t===undefined)return-1;else if(t===n)continue;else return Y(t,n)}while(++r)};K.prototype.inc=function(e,r){switch(e){case"premajor":this.prerelease.length=0;this.patch=0;this.minor=0;this.major++;this.inc("pre",r);break;case"preminor":this.prerelease.length=0;this.patch=0;this.minor++;this.inc("pre",r);break;case"prepatch":this.prerelease.length=0;this.inc("patch",r);this.inc("pre",r);break;case"prerelease":if(this.prerelease.length===0)this.inc("patch",r);this.inc("pre",r);break;case"major":if(this.minor!==0||this.patch!==0||this.prerelease.length===0)this.major++;this.minor=0;this.patch=0;this.prerelease=[];break;case"minor":if(this.patch!==0||this.prerelease.length===0)this.minor++;this.patch=0;this.prerelease=[];break;case"patch":if(this.prerelease.length===0)this.patch++;this.prerelease=[];break;case"pre":if(this.prerelease.length===0)this.prerelease=[0];else{var t=this.prerelease.length;while(--t>=0){if(typeof this.prerelease[t]==="number"){this.prerelease[t]++;t=-2}}if(t===-1)this.prerelease.push(0)}if(r){if(this.prerelease[0]===r){if(isNaN(this.prerelease[1]))this.prerelease=[r,0]}else this.prerelease=[r,0]}break;default:throw new Error("invalid increment argument: "+e)}this.format();return this};e.inc=Q;function Q(e,r,t,n){if(typeof t==="string"){n=t;t=undefined}try{return new K(e,t).inc(r,n).version}catch(i){return null}}e.diff=U;function U(e,r){if(pr(e,r)){return null}else{var t=D(e);var n=D(r);if(t.prerelease.length||n.prerelease.length){for(var i in t){if(i==="major"||i==="minor"||i==="patch"){if(t[i]!==n[i]){return"pre"+i}}}return"prerelease"}for(var i in t){if(i==="major"||i==="minor"||i==="patch"){if(t[i]!==n[i]){return i}}}}}e.compareIdentifiers=Y;var W=/^[0-9]+$/;function Y(e,r){var t=W.test(e);var n=W.test(r);if(t&&n){e=+e;r=+r}return t&&!n?-1:n&&!t?1:er?1:0}e.rcompareIdentifiers=er;function er(e,r){return Y(r,e)}e.major=rr;function rr(e,r){return new K(e,r).major}e.minor=tr;function tr(e,r){return new K(e,r).minor}e.patch=nr;function nr(e,r){return new K(e,r).patch}e.compare=ir;function ir(e,r,t){return new K(e,t).compare(r)}e.compareLoose=sr;function sr(e,r){return ir(e,r,true)}e.rcompare=or;function or(e,r,t){return ir(r,e,t)}e.sort=ar;function ar(r,t){return r.sort(function(r,n){return e.compare(r,n,t)})}e.rsort=fr;function fr(r,t){return r.sort(function(r,n){return e.rcompare(r,n,t)})}e.gt=ur;function ur(e,r,t){return ir(e,r,t)>0}e.lt=lr;function lr(e,r,t){return ir(e,r,t)<0}e.eq=pr;function pr(e,r,t){return ir(e,r,t)===0}e.neq=hr;function hr(e,r,t){return ir(e,r,t)!==0}e.gte=cr;function cr(e,r,t){return ir(e,r,t)>=0}e.lte=vr;function vr(e,r,t){return ir(e,r,t)<=0}e.cmp=mr;function mr(e,r,t,n){var i;switch(r){case"===":if(typeof e==="object")e=e.version;if(typeof t==="object")t=t.version;i=e===t;break;case"!==":if(typeof e==="object")e=e.version;if(typeof t==="object")t=t.version;i=e!==t;break;case"":case"=":case"==":i=pr(e,t,n);break;case"!=":i=hr(e,t,n);break;case">":i=ur(e,t,n);break;case">=":i=cr(e,t,n);break;case"<":i=lr(e,t,n);break;case"<=":i=vr(e,t,n);break;default:throw new TypeError("Invalid operator: "+r)}return i}e.Comparator=gr;function gr(e,r){if(e instanceof gr){if(e.loose===r)return e;else e=e.value}if(!(this instanceof gr))return new gr(e,r);this.loose=r;this.parse(e);if(this.semver===wr)this.value="";else this.value=this.operator+this.semver.version}var wr={};gr.prototype.parse=function(e){var r=this.loose?n[X]:n[Z];var t=e.match(r);if(!t)throw new TypeError("Invalid comparator: "+e);this.operator=t[1];if(this.operator==="=")this.operator="";if(!t[2])this.semver=wr;else this.semver=new K(t[2],this.loose)};gr.prototype.inspect=function(){return''};gr.prototype.toString=function(){return this.value};gr.prototype.test=function(e){if(this.semver===wr)return true;if(typeof e==="string")e=new K(e,this.loose);return mr(e,this.operator,this.semver,this.loose)};e.Range=yr;function yr(e,r){if(e instanceof yr&&e.loose===r)return e;if(!(this instanceof yr))return new yr(e,r);this.loose=r;this.raw=e;this.set=e.split(/\s*\|\|\s*/).map(function(e){return this.parseRange(e.trim())},this).filter(function(e){return e.length});if(!this.set.length){throw new TypeError("Invalid SemVer Range: "+e)}this.format()}yr.prototype.inspect=function(){return''};yr.prototype.format=function(){this.range=this.set.map(function(e){return e.join(" ").trim()}).join("||").trim();return this.range};yr.prototype.toString=function(){return this.range};yr.prototype.parseRange=function(e){var r=this.loose;e=e.trim();var t=r?n[G]:n[F];e=e.replace(t,Tr);e=e.replace(n[q],L);e=e.replace(n[T],V);e=e.replace(n[N],_);e=e.split(/\s+/).join(" ");var i=r?n[X]:n[Z];var s=e.split(" ").map(function(e){return jr(e,r)}).join(" ").split(/\s+/);if(this.loose){s=s.filter(function(e){return!!e.match(i)})}s=s.map(function(e){return new gr(e,r)});return s};e.toComparators=dr;function dr(e,r){return new yr(e,r).set.map(function(e){return e.map(function(e){return e.value}).join(" ").trim().split(" ")})}function jr(e,r){e=kr(e,r);e=Er(e,r);e=Sr(e,r);e=Ir(e,r);return e}function br(e){return!e||e.toLowerCase()==="x"||e==="*"}function Er(e,r){return e.trim().split(/\s+/).map(function(e){return $r(e,r)}).join(" ")}function $r(e,r){var t=r?n[C]:n[A];return e.replace(t,function(e,r,t,n,i){var s;if(br(r))s="";else if(br(t))s=">="+r+".0.0 <"+(+r+1)+".0.0";else if(br(n))s=">="+r+"."+t+".0 <"+r+"."+(+t+1)+".0";else if(i){if(i.charAt(0)!=="-")i="-"+i;s=">="+r+"."+t+"."+n+i+" <"+r+"."+(+t+1)+".0"}else s=">="+r+"."+t+"."+n+" <"+r+"."+(+t+1)+".0";return s})}function kr(e,r){return e.trim().split(/\s+/).map(function(e){return Rr(e,r)}).join(" ")}function Rr(e,r){var t=r?n[P]:n[z];return e.replace(t,function(e,r,t,n,i){var s;if(br(r))s="";else if(br(t))s=">="+r+".0.0 <"+(+r+1)+".0.0";else if(br(n)){if(r==="0")s=">="+r+"."+t+".0 <"+r+"."+(+t+1)+".0";else s=">="+r+"."+t+".0 <"+(+r+1)+".0.0"}else if(i){if(i.charAt(0)!=="-")i="-"+i;if(r==="0"){if(t==="0")s=">="+r+"."+t+"."+n+i+" <"+r+"."+t+"."+(+n+1);else s=">="+r+"."+t+"."+n+i+" <"+r+"."+(+t+1)+".0"}else s=">="+r+"."+t+"."+n+i+" <"+(+r+1)+".0.0"}else{if(r==="0"){if(t==="0")s=">="+r+"."+t+"."+n+" <"+r+"."+t+"."+(+n+1);else s=">="+r+"."+t+"."+n+" <"+r+"."+(+t+1)+".0"}else s=">="+r+"."+t+"."+n+" <"+(+r+1)+".0.0"}return s})}function Sr(e,r){return e.split(/\s+/).map(function(e){return xr(e,r)}).join(" ")}function xr(e,r){e=e.trim();var t=r?n[x]:n[S];return e.replace(t,function(e,r,t,n,i,s){var o=br(t);var a=o||br(n);var f=a||br(i);var u=f;if(r==="="&&u)r="";if(o){if(r===">"||r==="<"){e="<0.0.0"}else{e="*"}}else if(r&&u){if(a)n=0;if(f)i=0;if(r===">"){r=">=";if(a){t=+t+1;n=0;i=0}else if(f){n=+n+1;i=0}}else if(r==="<="){r="<";if(a)t=+t+1;else n=+n+1}e=r+t+"."+n+"."+i}else if(a){e=">="+t+".0.0 <"+(+t+1)+".0.0"}else if(f){e=">="+t+"."+n+".0 <"+t+"."+(+n+1)+".0"}return e})}function Ir(e,r){return e.trim().replace(n[O],"")}function Tr(e,r,t,n,i,s,o,a,f,u,l,p,h){if(br(t))r="";else if(br(n))r=">="+t+".0.0";else if(br(i))r=">="+t+"."+n+".0";else r=">="+r;if(br(f))a="";else if(br(u))a="<"+(+f+1)+".0.0";else if(br(l))a="<"+f+"."+(+u+1)+".0";else if(p)a="<="+f+"."+u+"."+l+"-"+p;else a="<="+a;return(r+" "+a).trim()}yr.prototype.test=function(e){if(!e)return false;if(typeof e==="string")e=new K(e,this.loose);for(var r=0;r0){var n=e[t].semver;if(n.major===r.major&&n.minor===r.minor&&n.patch===r.patch)return true}}return false}return true}e.satisfies=Ar;function Ar(e,r,t){try{r=new yr(r,t)}catch(n){return false}return r.test(e)}e.maxSatisfying=Cr;function Cr(e,r,t){return e.filter(function(e){return Ar(e,r,t)}).sort(function(e,r){return or(e,r,t)})[0]||null}e.validRange=Mr;function Mr(e,r){try{return new yr(e,r).range||"*"}catch(t){return null}}e.ltr=Nr;function Nr(e,r,t){return zr(e,r,"<",t)}e.gtr=_r;function _r(e,r,t){return zr(e,r,">",t)}e.outside=zr;function zr(e,r,t,n){e=new K(e,n);r=new yr(r,n);var i,s,o,a,f;switch(t){case">":i=ur;s=vr;o=lr;a=">";f=">=";break;case"<":i=lr;s=cr;o=ur;a="<";f="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(Ar(e,r,n)){return false}for(var u=0;u=b.f?e():a.fonts.load(fa(b.a),b.h).then(function(a){1<=a.length?c():setTimeout(k,25)},function(){e()})}k()}),e=new Promise(function(a,c){setTimeout(c,b.f)});Promise.race([e,c]).then(function(){b.g(b.a)},function(){b.j(b.a)})};function R(a,b,d,c,e,f,g){this.v=a;this.B=b;this.c=d;this.a=c;this.s=g||"BESbswy";this.f={};this.w=e||3E3;this.u=f||null;this.o=this.j=this.h=this.g=null;this.g=new N(this.c,this.s);this.h=new N(this.c,this.s);this.j=new N(this.c,this.s);this.o=new N(this.c,this.s);a=new H(this.a.c+",serif",K(this.a));a=P(a);this.g.a.style.cssText=a;a=new H(this.a.c+",sans-serif",K(this.a));a=P(a);this.h.a.style.cssText=a;a=new H("serif",K(this.a));a=P(a);this.j.a.style.cssText=a;a=new H("sans-serif",K(this.a));a= -P(a);this.o.a.style.cssText=a;O(this.g);O(this.h);O(this.j);O(this.o)}var S={D:"serif",C:"sans-serif"},T=null;function U(){if(null===T){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent);T=!!a&&(536>parseInt(a[1],10)||536===parseInt(a[1],10)&&11>=parseInt(a[2],10))}return T}R.prototype.start=function(){this.f.serif=this.j.a.offsetWidth;this.f["sans-serif"]=this.o.a.offsetWidth;this.A=q();la(this)}; -function ma(a,b,d){for(var c in S)if(S.hasOwnProperty(c)&&b===a.f[S[c]]&&d===a.f[S[c]])return!0;return!1}function la(a){var b=a.g.a.offsetWidth,d=a.h.a.offsetWidth,c;(c=b===a.f.serif&&d===a.f["sans-serif"])||(c=U()&&ma(a,b,d));c?q()-a.A>=a.w?U()&&ma(a,b,d)&&(null===a.u||a.u.hasOwnProperty(a.a.c))?V(a,a.v):V(a,a.B):na(a):V(a,a.v)}function na(a){setTimeout(p(function(){la(this)},a),50)}function V(a,b){setTimeout(p(function(){v(this.g.a);v(this.h.a);v(this.j.a);v(this.o.a);b(this.a)},a),0)};function W(a,b,d){this.c=a;this.a=b;this.f=0;this.o=this.j=!1;this.s=d}var X=null;W.prototype.g=function(a){var b=this.a;b.g&&w(b.f,[b.a.c("wf",a.c,K(a).toString(),"active")],[b.a.c("wf",a.c,K(a).toString(),"loading"),b.a.c("wf",a.c,K(a).toString(),"inactive")]);L(b,"fontactive",a);this.o=!0;oa(this)}; -W.prototype.h=function(a){var b=this.a;if(b.g){var d=y(b.f,b.a.c("wf",a.c,K(a).toString(),"active")),c=[],e=[b.a.c("wf",a.c,K(a).toString(),"loading")];d||c.push(b.a.c("wf",a.c,K(a).toString(),"inactive"));w(b.f,c,e)}L(b,"fontinactive",a);oa(this)};function oa(a){0==--a.f&&a.j&&(a.o?(a=a.a,a.g&&w(a.f,[a.a.c("wf","active")],[a.a.c("wf","loading"),a.a.c("wf","inactive")]),L(a,"active")):M(a.a))};function pa(a){this.j=a;this.a=new ja;this.h=0;this.f=this.g=!0}pa.prototype.load=function(a){this.c=new ca(this.j,a.context||this.j);this.g=!1!==a.events;this.f=!1!==a.classes;qa(this,new ha(this.c,a),a)}; -function ra(a,b,d,c,e){var f=0==--a.h;(a.f||a.g)&&setTimeout(function(){var a=e||null,k=c||null||{};if(0===d.length&&f)M(b.a);else{b.f+=d.length;f&&(b.j=f);var h,m=[];for(h=0;h { - console.debug("Error connecting to database: " + err.message); -}); - -import "./models/accounts"; -import "./models/codes"; -import "./models/deposits"; -import "./models/invoices"; -import "./models/keys"; -import "./models/payments"; -import "./models/orders"; -import "./models/requests"; -import "./models/users"; -import "./models/withdrawals"; - -import "./models/referrals"; -import "./models/waiting_list"; - -const { User, Account, Payment, Invoice, Key, Order, Referral, Request } = db; - -User.hasMany(Account, { - as: "accounts", - foreignKey: "user_id" -}); - -User.hasMany(Invoice, { - as: "invoices", - foreignKey: "user_id" -}); - -User.hasMany(Key, { - as: "keys", - foreignKey: "user_id" -}); - -User.hasMany(Order, { - as: "orders", - foreignKey: "user_id" -}); - -User.hasMany(Payment, { - as: "payments", - foreignKey: "user_id" -}); - -User.belongsTo(Account, { - as: "account", - foreignKey: "account_id" -}); - -Invoice.belongsTo(Account, { - as: "account", - foreignKey: "account_id" -}); - -Account.belongsTo(User, { - as: "user", - foreignKey: "user_id" -}); - -Invoice.belongsTo(User, { - as: "user", - foreignKey: "user_id" -}); - -Key.belongsTo(User, { - as: "user", - foreignKey: "user_id" -}); - -Payment.belongsTo(Account, { - as: "account", - foreignKey: "account_id" -}); - -Payment.belongsTo(User, { - as: "user", - foreignKey: "user_id" -}); - -Payment.belongsTo(Invoice, { - as: "invoice", - foreignKey: "invoice_id" -}); - -Order.belongsTo(User, { - as: "user", - foreignKey: "user_id" -}); - -Order.belongsTo(Account, { - as: "acc1", - foreignKey: "a1_id" -}); - -Order.belongsTo(Account, { - as: "acc2", - foreignKey: "a2_id" -}); - -User.hasMany(Referral, { - as: "referral_codes", - foreignKey: "sponsor_id" -}); - -User.hasMany(Referral, { - as: "sponsors", - foreignKey: "user_id" -}); - -Referral.belongsTo(User, { - as: "sponsor", - foreignKey: "sponsor_id" -}); - -Referral.belongsTo(User, { - as: "user", - foreignKey: "user_id" -}); - -Payment.belongsTo(Payment, { - as: "fee_payment", - foreignKey: "fee_payment_id" -}); - -Payment.belongsTo(User, { - as: "with", - foreignKey: "with_id" -}); - -Request.belongsTo(User, { - as: "requester", - foreignKey: "requester_id" -}); - -Request.belongsTo(User, { - as: "recipient", - foreignKey: "recipient_id" -}); - -Request.belongsTo(Invoice, { - as: "invoice", - foreignKey: "invoice_id" -}); - -Invoice.hasOne(Request, { - as: "request", - foreignKey: "invoice_id" -}); - -db.Order = Order; - -export default db; diff --git a/db/init/prodschema.sql b/db/init/prodschema.sql deleted file mode 100644 index 38e25324..00000000 --- a/db/init/prodschema.sql +++ /dev/null @@ -1,312 +0,0 @@ --- MySQL dump 10.14 Distrib 5.5.68-MariaDB, for Linux (x86_64) --- --- Host: localhost Database: coinos --- ------------------------------------------------------ --- Server version 5.5.68-MariaDB - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `SequelizeMeta` --- - -DROP TABLE IF EXISTS `SequelizeMeta`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `SequelizeMeta` ( - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - PRIMARY KEY (`name`), - UNIQUE KEY `name` (`name`), - UNIQUE KEY `SequelizeMeta_name_unique` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `accounts` --- - -DROP TABLE IF EXISTS `accounts`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `accounts` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `asset` varchar(255) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `balance` double DEFAULT NULL, - `pending` double DEFAULT NULL, - `name` varchar(255) DEFAULT NULL, - `ticker` varchar(255) DEFAULT NULL, - `precision` int(11) DEFAULT NULL, - `domain` varchar(255) DEFAULT NULL, - `contract` text, - `index` int(11) NOT NULL DEFAULT '0', - `pubkey` varchar(255) DEFAULT NULL, - `hide` tinyint(1) DEFAULT NULL, - `seed` varchar(255) DEFAULT NULL, - `path` varchar(255) DEFAULT NULL, - `network` varchar(255) DEFAULT NULL, - `privkey` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `asset` (`asset`) -) ENGINE=InnoDB AUTO_INCREMENT=20553 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `cheaters` --- - -DROP TABLE IF EXISTS `cheaters`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cheaters` ( - `id` int(11) NOT NULL DEFAULT '0', - `username` varchar(255) DEFAULT NULL, - `sum` double DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `codes` --- - -DROP TABLE IF EXISTS `codes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `codes` ( - `code` varchar(255) NOT NULL DEFAULT '', - `text` text, - PRIMARY KEY (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `deposits` --- - -DROP TABLE IF EXISTS `deposits`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `deposits` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `amount` double DEFAULT NULL, - `credited` tinyint(1) DEFAULT NULL, - `code` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4554 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `invoices` --- - -DROP TABLE IF EXISTS `invoices`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `invoices` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `text` text, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `rate` double DEFAULT NULL, - `currency` varchar(255) NOT NULL, - `address` varchar(255) DEFAULT NULL, - `received` double DEFAULT NULL, - `amount` double DEFAULT NULL, - `tip` double DEFAULT NULL, - `network` varchar(255) DEFAULT NULL, - `unconfidential` varchar(255) DEFAULT NULL, - `uuid` varchar(255) DEFAULT NULL, - `memo` text, - `account_id` int(11) DEFAULT NULL, - `path` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `part_of_unconfidential` (`unconfidential`(10)), - KEY `text_index` (`text`(100)), - KEY `unconfidential_index` (`unconfidential`(100)), - KEY `address_index` (`address`(100)) -) ENGINE=InnoDB AUTO_INCREMENT=36355 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `linkingkeys` --- - -DROP TABLE IF EXISTS `linkingkeys`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `linkingkeys` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `hex` varchar(255) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=11822 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `naughty` --- - -DROP TABLE IF EXISTS `naughty`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `naughty` ( - `id` int(11) NOT NULL DEFAULT '0', - `username` varchar(255) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `orders` --- - -DROP TABLE IF EXISTS `orders`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `orders` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `a1` varchar(255) DEFAULT NULL, - `a2` varchar(255) DEFAULT NULL, - `v1` double DEFAULT NULL, - `v2` double DEFAULT NULL, - `user_id` int(11) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `accepted` tinyint(1) NOT NULL DEFAULT '0', - `a1_id` int(11) DEFAULT NULL, - `a2_id` int(11) DEFAULT NULL, - `rate` double AS (v2/v1) VIRTUAL, - `completedAt` datetime DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=389 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `payments` --- - -DROP TABLE IF EXISTS `payments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `payments` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `hash` text, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `rate` double DEFAULT NULL, - `currency` varchar(255) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `received` tinyint(1) DEFAULT NULL, - `amount` double DEFAULT NULL, - `tip` double NOT NULL DEFAULT '0', - `confirmed` tinyint(1) NOT NULL, - `fee` double NOT NULL DEFAULT '0', - `network` varchar(255) DEFAULT NULL, - `account_id` int(11) DEFAULT NULL, - `preimage` varchar(255) DEFAULT NULL, - `memo` text, - `redeemed` tinyint(1) NOT NULL DEFAULT '0', - `redeemcode` varchar(255) DEFAULT NULL, - `path` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `idx_redeemcode` (`redeemcode`) -) ENGINE=InnoDB AUTO_INCREMENT=25828 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `reset` --- - -DROP TABLE IF EXISTS `reset`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `reset` ( - `password` varchar(255) DEFAULT NULL, - `seed` varchar(255) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `users` --- - -DROP TABLE IF EXISTS `users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `username` varchar(255) DEFAULT NULL, - `password` varchar(255) DEFAULT NULL, - `unit` varchar(255) DEFAULT 'SAT', - `symbol` varchar(255) DEFAULT NULL, - `currency` varchar(255) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `twofa` tinyint(1) DEFAULT NULL, - `pin` varchar(255) DEFAULT NULL, - `currencies` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, - `otpsecret` varchar(255) DEFAULT NULL, - `account_id` int(11) DEFAULT NULL, - `ip` int(10) unsigned DEFAULT NULL, - `subscriptions` text, - `seed` varchar(255) DEFAULT NULL, - `fiat` tinyint(1) NOT NULL DEFAULT '0', - `index` int(11) NOT NULL DEFAULT '0', - `verified` varchar(255) DEFAULT NULL, - `locked` tinyint(1) DEFAULT '0', - PRIMARY KEY (`id`), - KEY `ip` (`ip`), - KEY `username` (`username`) -) ENGINE=InnoDB AUTO_INCREMENT=14579 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `withdrawals` --- - -DROP TABLE IF EXISTS `withdrawals`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `withdrawals` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `amount` double DEFAULT NULL, - `completed` tinyint(1) DEFAULT NULL, - `transit` varchar(255) DEFAULT NULL, - `account` varchar(255) DEFAULT NULL, - `institution` varchar(255) DEFAULT NULL, - `notes` text, - `email` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4118 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2021-07-15 1:26:35 diff --git a/db/migrations/20210612181508_init_referrals.js b/db/migrations/20210612181508_init_referrals.js deleted file mode 100644 index 63b20ce4..00000000 --- a/db/migrations/20210612181508_init_referrals.js +++ /dev/null @@ -1,24 +0,0 @@ -export const up = function(knex) { - return Promise.all([ - knex.schema.dropTableIfExists('referrals'), - - knex.schema.createTable('referrals', function(t) { - t.increments('id').unsigned().primary(); - t.integer('user_id').nullable(); - t.foreign('user_id').references('users.id'); - - t.integer('sponsor_id').notNull(); - t.foreign('sponsor_id').references('users.id'); - - t.string('token').notNull(); - t.string('expiry').nullable(); - t.enum('status', ['available', 'used', 'expired', 'cancelled']).notNull().defaultTo('available'); - - t.timestamps(true, true); - }) - ]) -}; - -export const down = function(knex) { - return knex.schema.dropTable('referrals'); -}; diff --git a/db/migrations/20210612192804_init_waiting_list.js.js b/db/migrations/20210612192804_init_waiting_list.js.js deleted file mode 100644 index 0cef7356..00000000 --- a/db/migrations/20210612192804_init_waiting_list.js.js +++ /dev/null @@ -1,24 +0,0 @@ -export const up = function(knex) { - - return Promise.all([ - knex.schema.dropTableIfExists('waiting_list'), - - knex.schema.createTable('waiting_list', function(t) { - t.increments('id').unsigned().primary(); - - t.string('email').notNull(); - t.string('phone').notNull(); - t.enu('status', ['pending', 'activated', 'expired', 'cancelled']).notNull(); - - t.integer('user_id').nullable(); - t.foreign('user_id').references('users.id'); - t.text('notes').nullable(); - - t.timestamps(true, true); - }) - ]) -}; - -export const down = function(knex) { - return knex.schema.dropTable('waiting_list'); -}; diff --git a/db/migrations/20210612203333_schema_diff_merges.js b/db/migrations/20210612203333_schema_diff_merges.js deleted file mode 100644 index 418105d2..00000000 --- a/db/migrations/20210612203333_schema_diff_merges.js +++ /dev/null @@ -1,39 +0,0 @@ -export const up = function(knex) { - return Promise.all([ - knex.schema.dropTableIfExists('reset'), - knex.schema.dropTableIfExists('naughty'), - knex.schema.dropTableIfExists('cheaters'), - - knex.raw("UPDATE accounts set createdAt = '2020-01-01' where createdAt < '2020-01-01'"), - knex.raw("UPDATE accounts set updatedAt = '2020-01-01' where updatedAt < '2020-01-01'"), - knex.raw("ALTER TABLE accounts modify `contract` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL"), - - knex.schema.alterTable('orders', function(t) { - t.dropColumn('a1'); - t.dropColumn('a2'); - }), - - knex.schema.alterTable("users", function(t) { - t.string('authyId').nullable(); - t.dropColumn('symbol'); - }), - - knex.raw("ALTER TABLE users MODIFY `subscriptions` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL"), - - ]) -}; - -export const down = async function(knex) { - return Promise.all([ - knex.schema.table("orders", function(t) { - t.integer("a1"); - t.integer("a2") - }), - - knex.schema.table("users", function(t) { - t.string("symbol"); - t.dropColumn("unit"); - t.dropColumn("authyId"); - }), - ]) -}; \ No newline at end of file diff --git a/db/migrations/20210625232223_add_invoice_id_to_payments.js b/db/migrations/20210625232223_add_invoice_id_to_payments.js deleted file mode 100644 index adf99d24..00000000 --- a/db/migrations/20210625232223_add_invoice_id_to_payments.js +++ /dev/null @@ -1,13 +0,0 @@ -export const up = async function (knex) { - return knex.schema.table("payments", function(table) { - table.integer("invoice_id"); - table.foreign("invoice_id").references("invoices.id"); - }) -}; - -export const down = async function (knex) { - return knex.schema.table("payments", function(table) { - table.dropForeign("invoice_id"); - table.dropColumn("invoice_id"); - }) -}; diff --git a/db/migrations/20210628221727_add_webhook_to_invoices.js b/db/migrations/20210628221727_add_webhook_to_invoices.js deleted file mode 100644 index 434819e5..00000000 --- a/db/migrations/20210628221727_add_webhook_to_invoices.js +++ /dev/null @@ -1,11 +0,0 @@ -export const up = async function(knex) { - return knex.schema.table("invoices", function(table) { - table.text("webhook"); - }); -}; - -export const down = async function(knex) { - return knex.schema.table("invoices", function(table) { - table.dropColumn("webhook"); - }); -}; diff --git a/db/migrations/20210713174217_add_admin_to_users.js b/db/migrations/20210713174217_add_admin_to_users.js deleted file mode 100644 index 879c8cb7..00000000 --- a/db/migrations/20210713174217_add_admin_to_users.js +++ /dev/null @@ -1,16 +0,0 @@ -export const up = async function(knex) { - return knex.schema.table("users", function(t) { - t.boolean("admin"); - t.string('email'); - t.string('phone'); - - }); -}; - -export const down = async function(knex) { - return knex.schema.table("users", function(t) { - t.dropColumn("admin"); - t.dropColumn('email') - t.dropColumn('phone') - }); -}; diff --git a/db/migrations/20210714123455_data_fixes_merge.js b/db/migrations/20210714123455_data_fixes_merge.js deleted file mode 100644 index dfe96509..00000000 --- a/db/migrations/20210714123455_data_fixes_merge.js +++ /dev/null @@ -1,22 +0,0 @@ -export const up = function(knex) { - return Promise.all([ - knex.raw("delete from invoices where invoices.account_id not in (select id from accounts)"), - knex.raw("delete from orders where orders.a1_id not in (select id from accounts) or orders.a2_id not in (select id from accounts)"), - knex.raw("update users set account_id = 10427 where id = 6262 and account_id = 10428"), - knex.raw("delete from payments where account_id not in (select id from accounts)"), - knex.raw("alter table orders drop rate"), - knex.raw("alter table orders add rate double"), - knex.raw("update orders set rate = v1/v2"), - - knex.raw('update accounts left join users on accounts.user_id = users.id set user_id = null where length(username) > 32'), - knex.raw('update accounts left join users on accounts.user_id = users.id set user_id = null where users.id is null'), - knex.raw('delete from accounts where user_id is null'), - knex.raw('delete from users where length(username) > 32') - ]) -}; - -export const down = function(knex) { - return Promise.all([ - - ]) -}; diff --git a/db/migrations/20210714123456_add_fks_merge.js b/db/migrations/20210714123456_add_fks_merge.js deleted file mode 100644 index 3d5f3e94..00000000 --- a/db/migrations/20210714123456_add_fks_merge.js +++ /dev/null @@ -1,45 +0,0 @@ -export const up = function(knex) { - return Promise.all([ - - // alter table invoices add foreign key (user_id) references users (id); - // alter table invoices add foreign key (account_id) references accounts (id); - knex.schema.alterTable("invoices", function(t) { - t.foreign('user_id').references('users.id') - t.foreign('account_id').references('accounts.id') - t.index('updatedAt') - }), - - // alter table payments add foreign key (user_id) references users (id); - // alter table payments add foreign key (account_id) references accounts (id); - knex.schema.alterTable("payments", function(t) { - t.foreign('user_id').references('users.id') - t.foreign('account_id').references('accounts.id') - t.index('updatedAt') - }), - - // alter table orders add foreign key (user_id) references users (id); - // alter table orders add foreign key (a1_id) references accounts (id); - // alter table orders add foreign key (a2_id) references accounts (id); - knex.schema.alterTable("orders", function(t) { - t.foreign('user_id').references('users.id') - t.foreign('a1_id').references('accounts.id') - t.foreign('a2_id').references('accounts.id') - t.index('updatedAt') - }), - - // alter table deposits add foreign key (user_id) references users (id) - knex.schema.alterTable("deposits", function(t) { - t.foreign('user_id').references('users.id') - t.index('updatedAt') - }), - - // alter table withdrawals add foreign key (user_id) references users (id) - knex.schema.alterTable("withdrawals", function(t) { - t.foreign('user_id').references('users.id') - t.index('updatedAt') - }), - ]) -}; - -export const down = function(knex) { -}; diff --git a/db/migrations/s2/user_adjustments.js b/db/migrations/s2/user_adjustments.js deleted file mode 100644 index cbe17977..00000000 --- a/db/migrations/s2/user_adjustments.js +++ /dev/null @@ -1,67 +0,0 @@ - -export const up = function(knex) { - return Promise.all([ - - // move security fields to separate t (potentially different access permissions) - - knex.schema.createTable('user_security', function (t) { - t.increments('id').unsigned().primary(); - t.int('user_id').notNull(); - t.foreign('user_id').references('users.user_id'); - - t.boolean('twofa').notNull().defaultTo(false); - t.string('pin').notNull(); - t.string('otp_secret').nullable(); - t.string('seed').nullable(); - }), - - knex.raw("INSERT INTO user_security (user_id, twofa, pin, otp_secret, seed) SELECT users.id, twofa, pin, otpsecret, seed FROM users"), - - // move user-customizeable fields to separate t (potentially different access permissions) - - knex.schema.createTable('user_preferences', function (t) { - t.increments('id').unsigned().primary(); - t.int('user_id').notNull(); - t.foreign('user_id').references('users.user_id'); - - t.enum('default_unit', ['SAT', 'BTC', 'CAD']).notNull().defaultTo('SAT'); - t.enum('default_currency', ['CAD', 'USD']).notNull().defaultTo('CAD'); - t.enum('default_fiat', ['CAD', 'USD']).notNull().defaultTo('CAD'); - t.set('show_currencies', ['CAD', 'USD']).notNull().defaultTo('CAD'); - }), - - knex.raw("INSERT INTO user_preferences (user_id, default_unit, default_currency, default_fiat, show_currencies) SELECT users.id, unit, currency, fiat, currencies FROM users"), - - // remove deprecated fields - - knex.schema.table('users', function (t) { - t.string('uuid'); - t.enum('access', ['Anonymous', 'Member', 'Admin']).defaultTo('Anonymous'); - - t.dropColumn('account_id'); - t.dropColumn('unit'); - t.dropColumn('fiat'); - t.dropColumn('currency'); - t.dropColumn('currencies'); - }) - ]) - -}; - -export const down = function(knex) { - return Promise.all([ - knex.schema.table('users', function (t) { - t.dropColumn('uuid'); - t.dropColumn('access'); - - t.int('account_id'); - t.string('unit'); - t.string('fiat'); - t.string('currency'); - t.string('currencies'); - }), - - knex.schema.dropTable('user_preferences'), - knex.schema.dropTable('user_security'), - ]); -}; diff --git a/db/models/accounts.js b/db/models/accounts.js deleted file mode 100644 index 9485bc99..00000000 --- a/db/models/accounts.js +++ /dev/null @@ -1,217 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - path: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "path" - }, - seed: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "seed" - }, - network: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "network" - }, - name: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "name" - }, - contract: { - type: DataTypes.TEXT, - get: function() { - return JSON.parse(this.getDataValue("contract")); - }, - set: function(value) { - return this.setDataValue("contract", JSON.stringify(value)); - }, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "contract" - }, - domain: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "domain" - }, - ticker: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "ticker" - }, - asset: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "asset" - }, - balance: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "balance" - }, - btc_credits: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: 0.0, - primaryKey: false, - autoIncrement: false, - comment: "Credits that can be used towards paying Bitcoin withdrawal fees", - field: "btc_credits" - }, - liquid_credits: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: 0.0, - primaryKey: false, - autoIncrement: false, - comment: "Credits that can be used towards paying Liquid withdrawal fees", - field: "liquid_credits" - }, - lightning_credits: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: 0.0, - primaryKey: false, - autoIncrement: false, - comment: "Credits that can be used towards paying Lightning withdrawal fees", - field: "lightning_credits" - }, - pending: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "pending" - }, - hide: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "hide" - }, - index: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "index" - }, - privkey: { - type: DataTypes.STRING, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "privkey" - }, - pubkey: { - type: DataTypes.STRING, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "pubkey" - }, - precision: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "precision" - }, -}; - -const options = { - tableName: "accounts", - comment: "", - indexes: [] -}; - -db["Account"] = db.define("accounts_model", attributes, options); diff --git a/db/models/codes.js b/db/models/codes.js deleted file mode 100644 index 0981ac5e..00000000 --- a/db/models/codes.js +++ /dev/null @@ -1,32 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - code: { - type: DataTypes.STRING(255), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: false, - comment: null, - field: "code" - }, - text: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "text" - }, -}; - -const options = { - tableName: "codes", - comment: "", - indexes: [], - timestamps: false -}; - -db["Code"] = db.define("codes_model", attributes, options); diff --git a/db/models/deposits.js b/db/models/deposits.js deleted file mode 100644 index c486f468..00000000 --- a/db/models/deposits.js +++ /dev/null @@ -1,76 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - credited: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "credited" - }, - code: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "code" - }, -}; - -const options = { - tableName: "deposits", - comment: "", - indexes: [] -}; - -db["Deposit"] = db.define("deposits_model", attributes, options); diff --git a/db/models/invoices.js b/db/models/invoices.js deleted file mode 100644 index 82e64427..00000000 --- a/db/models/invoices.js +++ /dev/null @@ -1,202 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - account_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account_id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - text: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "text" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - path: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "path" - }, - rate: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "rate" - }, - uuid: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: DataTypes.UUIDV4, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "uuid" - }, - memo: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "memo" - }, - network: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "network" - }, - currency: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currency" - }, - unconfidential: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "unconfidential" - }, - address: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "address" - }, - received: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "received" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - pending: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "pending" - }, - webhook: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "webhook" - }, - status: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: "unpaid", - primaryKey: false, - autoIncrement: false, - comment: null, - field: "status" - }, - tip: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "tip" - }, - prompt: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "prompt" - }, -}; - -const options = { - tableName: "invoices", - comment: "", - indexes: [] -}; - -db["Invoice"] = db.define("invoices_model", attributes, options); diff --git a/db/models/keys.js b/db/models/keys.js deleted file mode 100644 index 5a5fda0c..00000000 --- a/db/models/keys.js +++ /dev/null @@ -1,40 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - hex: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "hex" - }, -}; - -const options = { - tableName: "linkingkeys", - comment: "", - indexes: [] -}; - -db["Key"] = db.define("keys_model", attributes, options); diff --git a/db/models/orders.js b/db/models/orders.js deleted file mode 100644 index 50a74c40..00000000 --- a/db/models/orders.js +++ /dev/null @@ -1,112 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - a1_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "a1_id" - }, - a2_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "a2_id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - completedAt: { - type: DataTypes.DATE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "completedAt" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - v1: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "v1" - }, - v2: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "v2" - }, - rate: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "rate" - }, - accepted: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "accepted" - }, -}; - -const options = { - tableName: "orders", - comment: "", - indexes: [] -}; - -db.Order = db.define("orders_model", attributes, options); diff --git a/db/models/payments.js b/db/models/payments.js deleted file mode 100644 index fac8ff56..00000000 --- a/db/models/payments.js +++ /dev/null @@ -1,220 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - with_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "with_id" - }, - account_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account_id" - }, - invoice_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "invoice_id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - path: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "path" - }, - memo: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "memo" - }, - hash: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "hash" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - rate: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "rate" - }, - preimage: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "preimage" - }, - network: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "network" - }, - currency: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currency" - }, - address: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "address" - }, - received: { - type: DataTypes.INTEGER(1), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "received" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - fee: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "fee" - }, - tip: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "tip" - }, - redeemed: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemed" - }, - redeemcode: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemcode" - }, - confirmed: { - type: DataTypes.INTEGER(1), - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "confirmed" - }, - fee_payment_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "fee_payment_id" - } -}; - -const options = { - tableName: "payments", - comment: "", - indexes: [] -}; - -db["Payment"] = db.define("payments_model", attributes, options); diff --git a/db/models/referrals.js b/db/models/referrals.js deleted file mode 100644 index 8aebde2e..00000000 --- a/db/models/referrals.js +++ /dev/null @@ -1,90 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - sponsor_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "sponsor_id" - }, - - token: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "token" - }, - expiry: { - type: DataTypes.DATE(), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "token" - }, - status: { - type: DataTypes.ENUM, - values: ['available', 'used', 'expired', 'cancelled'], - allowNull: true, - defaultValue: 'available', - primaryKey: false, - autoIncrement: false, - comment: null, - field: "token" - } -}; - -const options = { - tableName: "referrals", - comment: "", - indexes: [] -}; - -db["Referral"] = db.define("referrals_model", attributes, options); - -// exports.up = function(knex) { -// return knex.schema.createTable('referrals', function(t) { -// t.increments('id').unsigned().primary(); -// t.int('user_id').nullable(); -// t.foreign('user_id').references('users.id'); - -// t.int('sponsor_id').notNull(); -// t.foreign('sponsor_id').references('users.id'); - -// t.string('token').notNull(); -// t.string('expiry').nullable(); -// t.enum('status', ['available', 'used', 'expired', 'cancelled']).notNull().defaultTo('available'); - -// t.timestamps(); -// }) -// }; - -// exports.down = function(knex) { -// return knex.schema.dropTable('referrals'); -// }; diff --git a/db/models/requests.js b/db/models/requests.js deleted file mode 100644 index 76703a72..00000000 --- a/db/models/requests.js +++ /dev/null @@ -1,67 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - requester_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "requester_id" - }, - recipient_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "recipient_id" - }, - memo: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "memo" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, -}; - -const options = { - tableName: "requests", - comment: "", - indexes: [] -}; - -db["Request"] = db.define("requests_model", attributes, options); diff --git a/db/models/users.js b/db/models/users.js deleted file mode 100644 index fce79537..00000000 --- a/db/models/users.js +++ /dev/null @@ -1,270 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from "@sequelize/core"; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - uuid: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: DataTypes.UUIDV4, - primaryKey: true, - autoIncrement: false, - comment: null, - field: "uuid" - }, - address: { - type: DataTypes.STRING, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "address" - }, - theme: { - type: DataTypes.STRING, - allowNull: true, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "theme" - }, - profile: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "profile" - }, - banner: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "banner" - }, - admin: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "admin" - }, - locked: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "locked" - }, - verified: { - type: DataTypes.STRING, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "verified" - }, - fiat: { - type: DataTypes.BOOLEAN, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "fiat" - }, - index: { - type: DataTypes.INTEGER, - allowNull: true, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "index" - }, - ip: { - type: DataTypes.INTEGER.UNSIGNED, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "ip" - }, - seed: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "seed" - }, - username: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "username" - }, - password: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "password" - }, - unit: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: "SAT", - primaryKey: false, - autoIncrement: false, - comment: null, - field: "unit" - }, - account_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account_id" - }, - otpsecret: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: "CAD", - primaryKey: false, - autoIncrement: false, - comment: null, - field: "otpsecret" - }, - currency: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: "CAD", - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currency" - }, - currencies: { - type: DataTypes.JSON, - allowNull: false, - defaultValue: ["USD"], - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currencies" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - subscriptions: { - type: DataTypes.TEXT, - get: function() { - return JSON.parse(this.getDataValue("subscriptions")); - }, - set: function(value) { - return this.setDataValue("subscriptions", JSON.stringify(value)); - }, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "subscriptions" - }, - twofa: { - type: DataTypes.INTEGER(1), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "twofa" - }, - cipher: { - type: DataTypes.STRING(255) - }, - pubkey: { - type: DataTypes.STRING(255) - }, - salt: { - type: DataTypes.STRING(255) - }, - pin: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "pin" - }, - email: { - type: DataTypes.STRING(255) - }, - phone: { - type: DataTypes.STRING(255) - }, - locktime: { - type: DataTypes.INTEGER(11) - }, - prompt: { - type: DataTypes.BOOLEAN - }, - display: { - type: DataTypes.STRING(255) - } -}; - -const options = { - tableName: "users", - comment: "", - indexes: [], - charset: "utf8mb4", - collate: "utf8mb4_unicode_ci" -}; - -db["User"] = db.define("users_model", attributes, options); diff --git a/db/models/waiting_list.js b/db/models/waiting_list.js deleted file mode 100644 index 37f2889b..00000000 --- a/db/models/waiting_list.js +++ /dev/null @@ -1,45 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - primaryKey: true, - autoIncrement: true, - allowNull: false - }, - email: { - type: DataTypes.STRING(255), - validate: { - isEmail: true - } - }, - phone: { - type: DataTypes.STRING(255), - validate: { - is: /^[0-9]+$/ // replace spaces, brackets, dashes dynamically prior to create - } - }, - status: { - type: DataTypes.ENUM, - values: ['pending', 'activated', 'expired', 'cancelled'], - defaultValue: 'pending', - allowNull: false - } -} - -const options = { - tableName: "waiting_list", - comment: "Users on waiting list", - timestamps: true, - createdAt: 'created_at', - updatedAt: 'updated_at', - indexes: [ - { - unique: true, - fields: ['email', 'phone'] - } - ] -}; - -db["WaitingList"] = db.define("waiting_list_model", attributes, options); diff --git a/db/models/withdrawals.js b/db/models/withdrawals.js deleted file mode 100644 index fa2afb22..00000000 --- a/db/models/withdrawals.js +++ /dev/null @@ -1,103 +0,0 @@ -import db from "$db/db"; -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - transit: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "transit" - }, - institution: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "institution" - }, - account: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account" - }, - email: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "email" - }, - notes: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "notes" - }, -}; - -const options = { - tableName: "withdrawals", - comment: "", - indexes: [] -}; - -db["Withdrawal"] = db.define("withdrawals_model", attributes, options); diff --git a/db/normalized/README.md b/db/normalized/README.md deleted file mode 100644 index fd3cb0d3..00000000 --- a/db/normalized/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# General guidelines for Database schema: - -- String fields whose values are NOT open-ended (ie can contain random strings) should be changed to either: - - an FK to a separate lookup table (when the domain of options is large or can grow) - - an enum (when there are a short list of essentailly static options) - -- Field names should follow SQL standards: - - lower case - - underscore_separated_words - -- options for designated models should be standardized with: - { - tableName: "table_name", - comment: "description of table if not self-evident", - timestamps: true, - createdAt: 'created_at', - updatedAt: 'updated_at', - indexes: [ - { } - ] - }; - - ## Note regarding field attributes specified in model: - - - createdAt, updatedAt are renamed to abide by SQL standard. - - - by default allowNull is false, so this attribute need only be specified when fields are NOT NULL. - - - comment attribute should added to any field that is not self-evident - - - may need to add indexes to more fields for rapid access - - ## Notes regarding changes / updates - - - Some 1-1 tables are split up simply to separate scope or to control access permission (ie only user_preferences table may be edited by users themselves) - - ## Sequelize Operational Questions: - - Are indexes automatically generated by Sequelize migration for primary keys and/or foreign keys ? - - Does Sequelize add non-unique indexes for data mining if specified: eg { fields: ['username'] } - - Do timestamp fields require default value spec (eg Sequelize.NOW) ... or how can we define this in model ? - - ## Ongoing Questions: - - payments table needs to be investigated and refactored - - is invoices table required or is this just summary information ? - - orders table needs to be investigated and refactored - - need to add subscriptions table if applicable ( what is this comprised of ?) - - user_keys table should be commented to clarify application of each of these values. - diff --git a/db/normalized/accounts.js b/db/normalized/accounts.js deleted file mode 100644 index 8e5aad33..00000000 --- a/db/normalized/accounts.js +++ /dev/null @@ -1,231 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('accounts', function(t) { - t.increments('id').unsigned().primary(); - t.int('user_id').notNull(); - t.foreign('user_id').references('users.id'); - - t.int('uuid').notNull(); - - t.double('balance'); - t.double('pending'); - - t.enu('network', ['Bitcoin', 'Liquid', 'Lightning', 'CoinOS']).notNull(); - - t.longtext('contract').collate('utf8mb4').nullable(); // custodial ? - - // knex.raw("ALTER TABLE accounts MODIFY `contract` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL") - - // t.string('hide'); - // t.string('index'); - - t.int('liquid_asset_id').notNull(); - t.foreign('liquid_asset_id').references('liquid_assets.id'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('accounts'); -}; - -// Change -// -// move assets fields (asset, ticker, name, precision...) to separate liquid_assets reference - -// separate network specific fields to bitcoin_accounts / liquid_accounts / coinos_accounts tables ? or just non_custodial ? -// eg liquid -> liquid_asset info -// non-custodial ? -> privkey, pubkey, path, seed ? -// contract, hide, index ? -// - - -// const { DataTypes } = require('@sequelize/core'); - -// const attributes = { -// id: { -// type: DataTypes.INTEGER(11), -// allowNull: false, -// defaultValue: null, -// primaryKey: true, -// autoIncrement: true, -// comment: null, -// field: "id" -// }, -// user_id: { -// type: DataTypes.INTEGER(11), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "user_id" -// }, -// createdAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "createdAt" -// }, -// updatedAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "updatedAt" -// }, -// path: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "path" -// }, -// seed: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "seed" -// }, -// network: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "network" -// }, -// name: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "name" -// }, -// contract: { -// type: DataTypes.TEXT, -// get: function() { -// return JSON.parse(this.getDataValue("contract")); -// }, -// set: function(value) { -// return this.setDataValue("contract", JSON.stringify(value)); -// }, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "contract" -// }, -// domain: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "domain" -// }, -// ticker: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "ticker" -// }, -// asset: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "asset" -// }, -// balance: { -// type: DataTypes.DOUBLE, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "balance" -// }, -// pending: { -// type: DataTypes.DOUBLE, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "pending" -// }, -// hide: { -// type: DataTypes.BOOLEAN, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "hide" -// }, -// index: { -// type: DataTypes.INTEGER(11), -// allowNull: false, -// defaultValue: 0, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "index" -// }, -// privkey: { -// type: DataTypes.STRING, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "privkey" -// }, -// pubkey: { -// type: DataTypes.STRING, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "pubkey" -// }, -// precision: { -// type: DataTypes.INTEGER(11), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "precision" -// }, -// }; - -// const options = { -// tableName: "accounts", -// comment: "", -// indexes: [] -// }; - -// db["Account"] = db.define("accounts_model", attributes, options); diff --git a/db/normalized/bank_accounts.js b/db/normalized/bank_accounts.js deleted file mode 100644 index 5d8ea0dc..00000000 --- a/db/normalized/bank_accounts.js +++ /dev/null @@ -1,24 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('bank_accounts', function(t) { - t.increments('id').unsigned().primary(); - t.int('user_id').notNull(); - t.foreign('user_id').references('users.id'); - - t.int('transit_number'); - t.int('institution_number'); - t.int('account_number'); - - t.string('swift'); - t.string('iban'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('user_security'); -}; - -// Changes: -// -// bank info -> bank_account records diff --git a/db/normalized/credits.js b/db/normalized/credits.js deleted file mode 100644 index 5511b616..00000000 --- a/db/normalized/credits.js +++ /dev/null @@ -1,236 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('credits', function(t) { - t.increments('id').unsigned().primary(); - - t.enu('type', ['deposit', 'order', 'transfer', 'invoice', 'exchange']) - t.string('description') - - t.int('account_id').notNull(); - t.foreign('account_id').references('accounts.id'); - - t.double('amount'); - - t.enu('status', ['pending', 'completed', 'reversed', 'cancelled']) - - t.timestamps(); - }) -}; - -export const down = function(knex) { -return knex.schema.dropTable('credits'); -}; - -// Changes: -// -// Should these be tracked as transactions ? -// SAT -> SAT -// tracking various exchange variations (outside of external networks ?) -// -// Is user / account / amount info redundant ? -// + transaction_type: deposit / withdrawal / transfer / exchange ? -// -// add transfers table ? (for internal transfers only ?) -// add redemptions table ? (for bitcoin transfers only ?) -// add exchanges table ? (for conversion from one currency to another) -// -// network -> scope: internal / bitcoin / liquid / lightning ? -// add bitcoin_transaction ? (with redemption attributes) -// add liquid_transaction ? () -// add lightning_transaction ? (with LN_invoice_hash) -// add internal_transaction ? () -// -// context for path, memo, rate, preimage, address, received, fee, tip, confirmed, redeemcode ? - - -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - account_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account_id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - path: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "path" - }, - memo: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "memo" - }, - hash: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "hash" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - rate: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "rate" - }, - preimage: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "preimage" - }, - network: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "network" - }, - currency: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currency" - }, - address: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "address" - }, - received: { - type: DataTypes.INTEGER(1), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "received" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - fee: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "fee" - }, - tip: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "tip" - }, - redeemed: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemed" - }, - redeemcode: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemcode" - }, - confirmed: { - type: DataTypes.INTEGER(1), - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "confirmed" - } -}; - -const options = { - tableName: "payments", - comment: "", - indexes: [] -}; - -db["Payment"] = db.define("payments_model", attributes, options); diff --git a/db/normalized/debits.js b/db/normalized/debits.js deleted file mode 100644 index 6fcee974..00000000 --- a/db/normalized/debits.js +++ /dev/null @@ -1,236 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('debits', function(t) { - t.increments('id').unsigned().primary(); - - t.enu('type', ['withdrawal', 'order', 'transfer', 'invoice', 'exchange']) - - t.int('account_id').notNull(); - t.foreign('account_id').references('accounts.id'); - - t.double('amount'); - - t.enu('type', ['withdrawal', 'transfer', 'order', 'invoice']) - t.enu('status', ['pending', 'completed', 'reversed', 'cancelled']) - - t.timestamps(); - }) -}; - -export const down = function(knex) { -return knex.schema.dropTable('debits'); -}; - -// Changes: -// -// Should these be tracked as transactions ? -// SAT -> SAT -// tracking various exchange variations (outside of external networks ?) -// -// Is user / account / amount info redundant ? -// + transaction_type: deposit / withdrawal / transfer / exchange ? -// -// add transfers table ? (for internal transfers only ?) -// add redemptions table ? (for bitcoin transfers only ?) -// add exchanges table ? (for conversion from one currency to another) -// -// network -> scope: internal / bitcoin / liquid / lightning ? -// add bitcoin_transaction ? (with redemption attributes) -// add liquid_transaction ? () -// add lightning_transaction ? (with LN_invoice_hash) -// add internal_transaction ? () -// -// context for path, memo, rate, preimage, address, received, fee, tip, confirmed, redeemcode ? - - -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - account_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account_id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - path: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "path" - }, - memo: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "memo" - }, - hash: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "hash" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - rate: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "rate" - }, - preimage: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "preimage" - }, - network: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "network" - }, - currency: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currency" - }, - address: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "address" - }, - received: { - type: DataTypes.INTEGER(1), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "received" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - fee: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "fee" - }, - tip: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "tip" - }, - redeemed: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemed" - }, - redeemcode: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemcode" - }, - confirmed: { - type: DataTypes.INTEGER(1), - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "confirmed" - } -}; - -const options = { - tableName: "payments", - comment: "", - indexes: [] -}; - -db["Payment"] = db.define("payments_model", attributes, options); diff --git a/db/normalized/deposits.js b/db/normalized/deposits.js deleted file mode 100644 index 17a7c500..00000000 --- a/db/normalized/deposits.js +++ /dev/null @@ -1,100 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('deposits', function(t) { - t.increments('id').unsigned().primary(); - - t.int('credit_id').notNull(); - t.foreign('credit_id').references('credits.id'); - - t.string('confirmation_code'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('deposits'); -}; - -// Changes: -// -// reference transaction -// reference account rather than user -// credited - redundant with confirmation_code ? -// code -> confirmation_code - - -// const { DataTypes } = require('@sequelize/core'); - -// const attributes = { -// id: { -// type: DataTypes.INTEGER(11), -// allowNull: false, -// defaultValue: null, -// primaryKey: true, -// autoIncrement: true, -// comment: null, -// field: "id" -// }, -// amount: { -// type: DataTypes.DOUBLE, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "amount" -// }, -// user_id: { -// type: DataTypes.INTEGER(11), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "user_id" -// }, -// createdAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "createdAt" -// }, -// updatedAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "updatedAt" -// }, -// credited: { -// type: DataTypes.BOOLEAN, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "credited" -// }, -// code: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "code" -// }, -// }; - -// const options = { -// tableName: "deposits", -// comment: "", -// indexes: [] -// }; - -// db["Deposit"] = db.define("deposits_model", attributes, options); diff --git a/db/normalized/exchanges.js b/db/normalized/exchanges.js deleted file mode 100644 index 2be91990..00000000 --- a/db/normalized/exchanges.js +++ /dev/null @@ -1,232 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('exchanges', function(t) { - t.increments('id').unsigned().primary(); - - t.int('transfer_id').notNull(); - t.foreign('transfer_id').references('transfers.id'); - - t.double('exchange_rate').notNull() - t.double('exchange_fee').notNull().defaultTo(0) - - t.timestamps(); - }) -}; - -export const down = function(knex) { -return knex.schema.dropTable('exchanges'); -}; - -// Changes: -// -// Should these be tracked as transactions ? -// SAT -> SAT -// tracking various exchange variations (outside of external networks ?) -// -// Is user / account / amount info redundant ? -// + transaction_type: deposit / withdrawal / transfer / exchange ? -// -// add transfers table ? (for internal transfers only ?) -// add redemptions table ? (for bitcoin transfers only ?) -// add exchanges table ? (for conversion from one currency to another) -// -// network -> scope: internal / bitcoin / liquid / lightning ? -// add bitcoin_transaction ? (with redemption attributes) -// add liquid_transaction ? () -// add lightning_transaction ? (with LN_invoice_hash) -// add internal_transaction ? () -// -// context for path, memo, rate, preimage, address, received, fee, tip, confirmed, redeemcode ? - - -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - account_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account_id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - path: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "path" - }, - memo: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "memo" - }, - hash: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "hash" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - rate: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "rate" - }, - preimage: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "preimage" - }, - network: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "network" - }, - currency: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currency" - }, - address: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "address" - }, - received: { - type: DataTypes.INTEGER(1), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "received" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - fee: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "fee" - }, - tip: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "tip" - }, - redeemed: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemed" - }, - redeemcode: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemcode" - }, - confirmed: { - type: DataTypes.INTEGER(1), - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "confirmed" - } -}; - -const options = { - tableName: "payments", - comment: "", - indexes: [] -}; - -db["Payment"] = db.define("payments_model", attributes, options); diff --git a/db/normalized/invoices.js b/db/normalized/invoices.js deleted file mode 100644 index 823ad11d..00000000 --- a/db/normalized/invoices.js +++ /dev/null @@ -1,206 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('invoices', function(t) { - t.increments('id').unsigned().primary(); - - t.int('transfer_id').notNull(); - t.foreign('transfer_id').references('transfers.id'); - - t.enu('network', ['Bitcoin', 'Liquid', 'Lightning', 'CoinOS']).notNull(); - - t.string('bolt11'); // was 'text' - - // Clarify ??? - break out ? - context ? include everything encoded in hash... - t.double('amount'); - - // Exchanges - // ( are there a couple of types of invoices ? - eg checkout / voucher / receive ... with different attributes ?) - t.double('rate') - t.string('currency') - t.double('tip') - - t.string('path') - t.string('uuid') - t.string('memo') - t.string('unconfidential') - t.string('address') - t.string('received') - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('invoices'); -}; - - -// Variations: ? -// wallet -> receive - fields ? -// checkout - fields ? -// voucher - fields ? - -// const { DataTypes } = require('@sequelize/core'); - -// const attributes = { -// id: { -// type: DataTypes.INTEGER(11), -// allowNull: false, -// defaultValue: null, -// primaryKey: true, -// autoIncrement: true, -// comment: null, -// field: "id" -// }, -// account_id: { -// type: DataTypes.INTEGER(11), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "account_id" -// }, -// user_id: { -// type: DataTypes.INTEGER(11), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "user_id" -// }, -// text: { -// type: DataTypes.TEXT, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "text" -// }, -// createdAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "createdAt" -// }, -// updatedAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "updatedAt" -// }, -// path: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "path" -// }, -// rate: { -// type: DataTypes.DOUBLE, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "rate" -// }, -// uuid: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "uuid" -// }, -// memo: { -// type: DataTypes.TEXT, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "memo" -// }, -// network: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "network" -// }, -// currency: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "currency" -// }, -// unconfidential: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "unconfidential" -// }, -// address: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "address" -// }, -// received: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "received" -// }, -// amount: { -// type: DataTypes.DOUBLE, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "amount" -// }, -// tip: { -// type: DataTypes.DOUBLE, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "tip" -// }, -// }; - -// const options = { -// tableName: "invoices", -// comment: "", -// indexes: [] -// }; - -// db["Invoice"] = db.define("invoices_model", attributes, options); diff --git a/db/normalized/liquid_assets.js b/db/normalized/liquid_assets.js deleted file mode 100644 index fab96dc5..00000000 --- a/db/normalized/liquid_assets.js +++ /dev/null @@ -1,21 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('liquid_assets', function(t) { - t.increments('id').unsigned().primary(); - - t.string('asset_id').notNull(); - t.string('name').notNull(); - - t.string('domain') - t.string('ticker').notNull(); - t.int('precision').defaultTo(8); - t.string('issuer_pubkey') - - t.boolean('registered').defaultTo(false); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('liquid_assets'); -}; \ No newline at end of file diff --git a/db/normalized/ln_keys.js b/db/normalized/ln_keys.js deleted file mode 100644 index 7922e40e..00000000 --- a/db/normalized/ln_keys.js +++ /dev/null @@ -1,16 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('ln_keys', function(t) { - t.increments('id').unsigned().primary(); - - t.int('user_id').notNull(); - t.foreign('user_id').references('users.id'); - - t.string('hex').notNull(); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('ln_keys'); -}; \ No newline at end of file diff --git a/db/normalized/lnurl_codes.js b/db/normalized/lnurl_codes.js deleted file mode 100644 index 5ed81135..00000000 --- a/db/normalized/lnurl_codes.js +++ /dev/null @@ -1,14 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('lnurl_codes', function(t) { - t.increments('id').unsigned().primary(); - - t.string('code').notNull(); - t.text('text'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('lnurl_codes'); -}; diff --git a/db/normalized/non_custodial_accounts.js b/db/normalized/non_custodial_accounts.js deleted file mode 100644 index e8190ce6..00000000 --- a/db/normalized/non_custodial_accounts.js +++ /dev/null @@ -1,91 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('non_custodial_accounts', function(t) { - t.increments('id').unsigned().primary(); - t.int('account_id').notNull(); - t.foreign('account_id').references('accounts.id'); - - // what is the scope / context of these attributes ? - t.string('path'); - t.string('seed'); - // t.string('hide'); - // t.string('index'); - t.string('pub_key'); - t.string('priv_key'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('non_custodial_accounts'); -}; - -// const { DataTypes } = require('@sequelize/core'); - -// const attributes = { -// id: { -// type: DataTypes.INTEGER(11), -// primaryKey: true, -// autoIncrement: true, -// allowNull: false -// }, -// account_id: { -// type: DataTypes.INTEGER(11), -// references: { model: Account, key: 'id' }, -// allowNull: false, -// comment: "Reference to account" -// }, -// path: { -// type: DataTypes.STRING(255), -// comment: "What is this ?" -// }, -// seed: { -// type: DataTypes.STRING(255), -// comment: "What is this ?" -// }, -// network: { -// type: DataTypes.INTEGER(11), -// references: { model: Network, key: 'id' }, -// allowNull: false, -// comment: "Reference to network" -// }, -// domain: { -// type: DataTypes.STRING(255), -// comment: "What is this ? ... is it unique or an enum ?" -// }, -// ticker: { -// type: DataTypes.STRING(255), -// comment: "What is this ?" -// }, -// hide: { -// type: DataTypes.STRING(255), -// comment: "What is this ?" -// }, -// index: { -// type: DataTypes.STRING(255), -// comment: "What is this ?" -// }, -// pub_key: { -// type: DataTypes.STRING(255), -// comment: "What is this ?" -// }, -// priv_key: { -// type: DataTypes.STRING(255), -// comment: "What is this ?" -// } -// } -// const options = { -// tableName: "non_custodial_accounts", -// comment: "", -// timestamps: true, -// createdAt: 'created_at', -// updatedAt: 'updated_at', -// indexes: [ -// { -// unique: true, -// fields: ['account_id'] -// } -// ] -// }; - -// db["NonCustodialAccount"] = db.define("non_custodial_accounts_model", attributes, options); diff --git a/db/normalized/orders.js b/db/normalized/orders.js deleted file mode 100644 index 06e83db1..00000000 --- a/db/normalized/orders.js +++ /dev/null @@ -1,29 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('orders', function(t) { - t.increments('id').unsigned().primary(); - - t.int('sell_transfer_id').notNull(); - t.foreign('sell_transfer_id').references('transfers.id'); - - t.int('buy_transfer_id').notNull(); - t.foreign('buy_transfer_id').references('transfers.id'); - - // t.int('exchange_id').notNull(); - // t.foreign('exchange_id').references('exchanges.id'); - - t.double('exchange_rate').notNull(); - // t.double('amount').notNull(); - - t.enu('status', ['Submitted', 'Accepted', 'Cancelled']).notNull(); - t.date('completion_date') - - t.timestamps(); - }) -}; - -// Bid / Accepted ... separate into Bid (credit + debit), Accepted (credit + debit) - -export const down = function(knex) { - return knex.schema.dropTable('orders'); -}; - diff --git a/db/normalized/referrals.js b/db/normalized/referrals.js deleted file mode 100644 index 7380f2cf..00000000 --- a/db/normalized/referrals.js +++ /dev/null @@ -1,20 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('referrals', function(t) { - t.increments('id').unsigned().primary(); - t.int('user_id').nullable(); - t.foreign('user_id').references('users.id'); - - t.int('sponsor_id').notNull(); - t.foreign('sponsor_id').references('users.id'); - - t.string('token').notNull(); - t.string('expiry').nullable(); - t.enum('status', ['available', 'used', 'expired', 'cancelled']).notNull().defaultTo('available'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('referrals'); -}; diff --git a/db/normalized/transactions.js b/db/normalized/transactions.js deleted file mode 100644 index 230faca4..00000000 --- a/db/normalized/transactions.js +++ /dev/null @@ -1,41 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('transactions', function(t) { - t.increments('id').unsigned().primary(); - - t.enu('type', ['deposit', 'withdrawal', 'transfer', 'exchange', 'invoice', 'voucher', 'new asset']) - - // t.text('notes'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { -return knex.schema.dropTable('transactions'); -}; - -// Changes: -// -// Deposit = deposit + credit -// Withdrawal = withdrawal + debit -// Transfer = credit + debit -// Exchange = credit + debit + debit + credit -// Invoice = invoice + debit ? -// Voucher = invoice + debit ? -// New Asset = liquid_asset + credit - -// -// context for path, memo, rate, preimage, address, received, fee, tip, confirmed, redeemcode ? -export const up = function(knex) { - return knex.schema.createTable('transactions', function(t) { - t.increments('id').unsigned().primary(); - - t.enu('type', ['deposit', 'withdrawal', 'order', 'transfer', 'invoice', 'voucher']) - - t.timestamps(); - }) -}; - -export const down = function(knex) { -return knex.schema.dropTable('transactions'); -}; \ No newline at end of file diff --git a/db/normalized/transfers.js b/db/normalized/transfers.js deleted file mode 100644 index 64f5c392..00000000 --- a/db/normalized/transfers.js +++ /dev/null @@ -1,243 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('transfers', function(t) { - t.increments('id').unsigned().primary(); - - t.int('transaction_id').notNull(); - t.foreign('transaction_id').references('accounts.id'); - - t.int('debit_id').notNull(); - t.foreign('debit_id').references('debits.id'); - - t.int('credit_id').notNull(); - t.foreign('credit_id').references('credits.id'); - - // t.int('liquid_asset_id').notNull(); - // t.foreign('liquid_asset_id').references('liquid_assets.id'); - - // t.double('amount'); - t.double('transfer_fee').notNull().defaultTo(0) - - t.enu('status', ['pending', 'completed', 'reversed', 'cancelled']) - - t.timestamps(); - }) -}; - -export const down = function(knex) { -return knex.schema.dropTable('transfers'); -}; - -// Changes: -// -// Should these be tracked as transactions ? -// SAT -> SAT -// tracking various exchange variations (outside of external networks ?) -// -// Is user / account / amount info redundant ? -// + transaction_type: deposit / withdrawal / transfer / exchange ? -// -// add transfers table ? (for internal transfers only ?) -// add redemptions table ? (for bitcoin transfers only ?) -// add exchanges table ? (for conversion from one currency to another) -// -// network -> scope: internal / bitcoin / liquid / lightning ? -// add bitcoin_transaction ? (with redemption attributes) -// add liquid_transaction ? () -// add lightning_transaction ? (with LN_invoice_hash) -// add internal_transaction ? () -// -// context for path, memo, rate, preimage, address, received, fee, tip, confirmed, redeemcode ? - - -import { DataTypes } from '@sequelize/core'; - -const attributes = { - id: { - type: DataTypes.INTEGER(11), - allowNull: false, - defaultValue: null, - primaryKey: true, - autoIncrement: true, - comment: null, - field: "id" - }, - account_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "account_id" - }, - user_id: { - type: DataTypes.INTEGER(11), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "user_id" - }, - path: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "path" - }, - memo: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "memo" - }, - hash: { - type: DataTypes.TEXT, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "hash" - }, - createdAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "createdAt" - }, - updatedAt: { - type: DataTypes.DATE, - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "updatedAt" - }, - rate: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "rate" - }, - preimage: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "preimage" - }, - network: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "network" - }, - currency: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "currency" - }, - address: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "address" - }, - received: { - type: DataTypes.INTEGER(1), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "received" - }, - amount: { - type: DataTypes.DOUBLE, - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "amount" - }, - fee: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "fee" - }, - tip: { - type: DataTypes.DOUBLE, - allowNull: false, - defaultValue: 0, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "tip" - }, - redeemed: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemed" - }, - redeemcode: { - type: DataTypes.STRING(255), - allowNull: true, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "redeemcode" - }, - confirmed: { - type: DataTypes.INTEGER(1), - allowNull: false, - defaultValue: null, - primaryKey: false, - autoIncrement: false, - comment: null, - field: "confirmed" - } -}; - -const options = { - tableName: "payments", - comment: "", - indexes: [] -}; - -db["Payment"] = db.define("payments_model", attributes, options); diff --git a/db/normalized/user_preferences.js b/db/normalized/user_preferences.js deleted file mode 100644 index 6af69f3f..00000000 --- a/db/normalized/user_preferences.js +++ /dev/null @@ -1,20 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('user_preferences', function(t) { - t.increments('id').unsigned().primary(); - t.int('user_id').notNull(); - t.foreign('user_id').references('users.id'); - - t.enum('default_unit', ['SAT', 'BTC', 'CAD']).notNull().defaultTo('SAT'); - t.enum('default_currency', ['CAD', 'USD']).notNull().defaultTo('CAD'); - t.enum('default_fiat', ['CAD', 'USD']).notNull().defaultTo('CAD'); - t.set('show_currencies', ['CAD', 'USD']).notNull().defaultTo('CAD'); - t.longtext(`subscriptions`).collate('utf8mb4').nullable(); // CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL") - t.timestamps(); - - // knex.raw("ALTER TABLE users MODIFY `subscriptions` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL") - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('user_preferences'); -}; diff --git a/db/normalized/user_security.js b/db/normalized/user_security.js deleted file mode 100644 index 919b2902..00000000 --- a/db/normalized/user_security.js +++ /dev/null @@ -1,22 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('user_security', function(t) { - t.increments('id').unsigned().primary(); - t.int('user_id').notNull(); - t.foreign('user_id').references('users.id'); - - t.boolean('twofa').notNull().defaultTo(false); - t.string('pin').notNull(); - t.string('otp_secret').nullable(); - t.string('seed').nullable(); - - t.boolean('email_verified').notNull().default(false) - t.boolean('phone_verified').notNull().default(false) - t.boolean('kyc_verified').notNull().default(false) - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('user_security'); -}; \ No newline at end of file diff --git a/db/normalized/users.js b/db/normalized/users.js deleted file mode 100644 index dd239f24..00000000 --- a/db/normalized/users.js +++ /dev/null @@ -1,18 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('users', function(t) { - t.increments('id').unsigned().primary(); - - t.string('uuid').notNull().index(); - t.string('username').notNull().index(); - t.string('password').notNull(); - t.string('email').nullable().index(); - t.string('phone').nullable(); - t.enum('access', ['Anonymous', 'Registered', 'Admin']).notNull().defaultTo('Anonymous'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('users'); -}; \ No newline at end of file diff --git a/db/normalized/vouchers.js b/db/normalized/vouchers.js deleted file mode 100644 index 35d54de2..00000000 --- a/db/normalized/vouchers.js +++ /dev/null @@ -1,18 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('vouchers', function(t) { - t.increments('id').unsigned().primary(); - - t.int('invoice_id').notNull(); - t.foreign('invoice_id').references('invoices.id'); - - t.string('redemption_code').notNull(); - - t.enu('status', ['pending', 'redeemed', 'cancelled']).notNull(); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('vouchers'); -}; \ No newline at end of file diff --git a/db/normalized/waiting_list.js b/db/normalized/waiting_list.js deleted file mode 100644 index 5a37dc52..00000000 --- a/db/normalized/waiting_list.js +++ /dev/null @@ -1,60 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('waiting_list', function(t) { - t.increments('id').unsigned().primary(); - - t.string('email').notNull(); - t.string('phone').notNull(); - t.enu('status', ['pending', 'activated', 'expired', 'cancelled']).notNull(); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('waiting_list'); -}; - -// const { DataTypes } = require('@sequelize/core'); - -// const attributes = { -// id: { -// type: DataTypes.INTEGER(11), -// primaryKey: true, -// autoIncrement: true, -// allowNull: false -// }, -// email: { -// type: DataTypes.STRING(255), -// validate: { -// isEmail: true -// } -// }, -// sms: { -// type: DataTypes.STRING(255), -// validate: { -// is: /^[0-9]+$/ // replace spaces, brackets, dashes dynamically prior to create -// } -// }, -// status: { -// type: DataTypes.ENUM, -// values: ['pending', 'activated', 'expired', 'cancelled'], -// defaultValue: 'pending', -// allowNull: false -// } -// } - -// const options = { -// tableName: "waiting_list", -// comment: "Users on waiting list", -// timestamps: true, -// createdAt: 'created_at', -// updatedAt: 'updated_at', -// indexes: [ -// { -// unique: true, -// fields: ['email', 'sms'] -// } -// ] -// }; - -// db["WaitingList"] = db.define("waiting_list_model", attributes, options); diff --git a/db/normalized/withdrawals.js b/db/normalized/withdrawals.js deleted file mode 100644 index 742f3dbf..00000000 --- a/db/normalized/withdrawals.js +++ /dev/null @@ -1,128 +0,0 @@ -export const up = function(knex) { - return knex.schema.createTable('withdrawals', function(t) { - t.increments('id').unsigned().primary(); - - t.int('debit_id').notNull(); - t.foreign('debit_id').references('debits.id'); - - t.int('bank_account_id').notNull(); - t.foreign('bank_account_id').references('bank_accounts.id'); - - t.double('withdrawal_fee').notNull().defaultTo(0) - - t.text('notes'); - - t.timestamps(); - }) -}; - -export const down = function(knex) { - return knex.schema.dropTable('withdrawals'); -}; - -// Changes: -// -// bank info -> bank_account records - -// const { DataTypes } = require('@sequelize/core'); - -// const attributes = { -// id: { -// type: DataTypes.INTEGER(11), -// allowNull: false, -// defaultValue: null, -// primaryKey: true, -// autoIncrement: true, -// comment: null, -// field: "id" -// }, -// amount: { -// type: DataTypes.DOUBLE, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "amount" -// }, -// user_id: { -// type: DataTypes.INTEGER(11), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "user_id" -// }, -// createdAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "createdAt" -// }, -// updatedAt: { -// type: DataTypes.DATE, -// allowNull: false, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "updatedAt" -// }, -// transit: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "transit" -// }, -// institution: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "institution" -// }, -// account: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "account" -// }, -// email: { -// type: DataTypes.STRING(255), -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "email" -// }, -// notes: { -// type: DataTypes.TEXT, -// allowNull: true, -// defaultValue: null, -// primaryKey: false, -// autoIncrement: false, -// comment: null, -// field: "notes" -// }, -// }; - -// const options = { -// tableName: "withdrawals", -// comment: "", -// indexes: [] -// }; - -// db["Withdrawal"] = db.define("withdrawals_model", attributes, options); diff --git a/db/schema.sql b/db/schema.sql deleted file mode 100644 index e6e2e03f..00000000 --- a/db/schema.sql +++ /dev/null @@ -1,479 +0,0 @@ --- MariaDB dump 10.19 Distrib 10.6.1-MariaDB, for debian-linux-gnu (x86_64) --- --- Host: localhost Database: coinos --- ------------------------------------------------------ --- Server version 10.6.1-MariaDB-1:10.6.1+maria~focal - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Current Database: `coinos` --- - -CREATE DATABASE /*!32312 IF NOT EXISTS*/ `coinos` /*!40100 DEFAULT CHARACTER SET latin1 */; - -USE `coinos`; - --- --- Table structure for table `SequelizeMeta` --- - -DROP TABLE IF EXISTS `SequelizeMeta`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `SequelizeMeta` ( - `name` varchar(255) COLLATE utf8mb3_unicode_ci NOT NULL, - PRIMARY KEY (`name`), - UNIQUE KEY `name` (`name`), - UNIQUE KEY `SequelizeMeta_name_unique` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `accounts` --- - -DROP TABLE IF EXISTS `accounts`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `accounts` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `asset` varchar(255) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `balance` double DEFAULT NULL, - `pending` double DEFAULT NULL, - `name` varchar(255) DEFAULT NULL, - `ticker` varchar(255) DEFAULT NULL, - `precision` int(11) DEFAULT NULL, - `domain` varchar(255) DEFAULT NULL, - `contract` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, - `index` int(11) NOT NULL DEFAULT 0, - `pubkey` varchar(255) DEFAULT NULL, - `hide` tinyint(1) DEFAULT NULL, - `seed` varchar(255) DEFAULT NULL, - `path` varchar(255) DEFAULT NULL, - `network` varchar(255) DEFAULT NULL, - `privkey` varchar(255) DEFAULT NULL, - `btc_credits` double NOT NULL DEFAULT 0, - `liquid_credits` double NOT NULL DEFAULT 0, - `lightning_credits` double NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - KEY `asset` (`asset`) -) ENGINE=InnoDB AUTO_INCREMENT=21826 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `codes` --- - -DROP TABLE IF EXISTS `codes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `codes` ( - `code` varchar(255) NOT NULL DEFAULT '', - `text` text DEFAULT NULL, - PRIMARY KEY (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `deposits` --- - -DROP TABLE IF EXISTS `deposits`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `deposits` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `amount` double DEFAULT NULL, - `credited` tinyint(1) DEFAULT NULL, - `code` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `deposits_user_id_foreign` (`user_id`), - CONSTRAINT `deposits_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4568 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `invoices` --- - -DROP TABLE IF EXISTS `invoices`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `invoices` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `text` text DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `rate` double DEFAULT NULL, - `currency` varchar(255) NOT NULL, - `address` varchar(255) DEFAULT NULL, - `received` bigint(20) NOT NULL DEFAULT 0, - `amount` bigint(20) DEFAULT NULL, - `tip` double DEFAULT NULL, - `network` varchar(255) DEFAULT NULL, - `unconfidential` varchar(255) DEFAULT NULL, - `uuid` varchar(255) DEFAULT uuid(), - `memo` text DEFAULT NULL, - `account_id` int(11) DEFAULT NULL, - `path` varchar(255) DEFAULT NULL, - `webhook` text DEFAULT NULL, - `status` varchar(255) DEFAULT 'unpaid', - `prompt` tinyint(1) NOT NULL DEFAULT 0, - `pending` bigint(20) DEFAULT 0, - PRIMARY KEY (`id`), - KEY `part_of_unconfidential` (`unconfidential`(10)), - KEY `text_index` (`text`(100)), - KEY `unconfidential_index` (`unconfidential`(100)), - KEY `address_index` (`address`(100)), - KEY `invoices_user_id_foreign` (`user_id`), - KEY `invoices_account_id_foreign` (`account_id`), - KEY `uuid` (`uuid`(10)), - CONSTRAINT `invoices_account_id_foreign` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`), - CONSTRAINT `invoices_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=49340 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `linkingkeys` --- - -DROP TABLE IF EXISTS `linkingkeys`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `linkingkeys` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `hex` varchar(255) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=12640 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `lnurl_migrations` --- - -DROP TABLE IF EXISTS `lnurl_migrations`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `lnurl_migrations` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(255) DEFAULT NULL, - `batch` int(11) DEFAULT NULL, - `migration_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `lnurl_migrations_lock` --- - -DROP TABLE IF EXISTS `lnurl_migrations_lock`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `lnurl_migrations_lock` ( - `index` int(10) unsigned NOT NULL AUTO_INCREMENT, - `is_locked` int(11) DEFAULT NULL, - PRIMARY KEY (`index`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `migrations` --- - -DROP TABLE IF EXISTS `migrations`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `migrations` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(255) DEFAULT NULL, - `batch` int(11) DEFAULT NULL, - `migration_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `migrations_lock` --- - -DROP TABLE IF EXISTS `migrations_lock`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `migrations_lock` ( - `index` int(10) unsigned NOT NULL AUTO_INCREMENT, - `is_locked` int(11) DEFAULT NULL, - PRIMARY KEY (`index`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `orders` --- - -DROP TABLE IF EXISTS `orders`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `orders` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `v1` double DEFAULT NULL, - `v2` double DEFAULT NULL, - `user_id` int(11) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `accepted` tinyint(1) NOT NULL DEFAULT 0, - `a1_id` int(11) DEFAULT NULL, - `a2_id` int(11) DEFAULT NULL, - `completedAt` datetime DEFAULT NULL, - `rate` double DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `orders_user_id_foreign` (`user_id`), - KEY `orders_a1_id_foreign` (`a1_id`), - KEY `orders_a2_id_foreign` (`a2_id`), - CONSTRAINT `orders_a1_id_foreign` FOREIGN KEY (`a1_id`) REFERENCES `accounts` (`id`), - CONSTRAINT `orders_a2_id_foreign` FOREIGN KEY (`a2_id`) REFERENCES `accounts` (`id`), - CONSTRAINT `orders_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=337 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `payments` --- - -DROP TABLE IF EXISTS `payments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `payments` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `hash` text DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `rate` double DEFAULT NULL, - `currency` varchar(255) DEFAULT NULL, - `address` varchar(255) DEFAULT NULL, - `received` tinyint(1) DEFAULT NULL, - `amount` double DEFAULT NULL, - `tip` double NOT NULL DEFAULT 0, - `confirmed` tinyint(1) NOT NULL, - `fee` double NOT NULL DEFAULT 0, - `network` varchar(255) DEFAULT NULL, - `account_id` int(11) DEFAULT NULL, - `preimage` varchar(255) DEFAULT NULL, - `memo` text DEFAULT NULL, - `redeemed` tinyint(1) NOT NULL DEFAULT 0, - `redeemcode` varchar(255) DEFAULT NULL, - `path` varchar(255) DEFAULT NULL, - `invoice_id` int(11) DEFAULT NULL, - `fee_payment_id` int(11) DEFAULT NULL, - `with_id` int(11) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `idx_redeemcode` (`redeemcode`), - KEY `payments_invoice_id_foreign` (`invoice_id`), - KEY `payments_user_id_foreign` (`user_id`), - KEY `payments_account_id_foreign` (`account_id`), - KEY `fk_payments` (`fee_payment_id`), - KEY `fk_with` (`with_id`), - CONSTRAINT `fk_payments` FOREIGN KEY (`fee_payment_id`) REFERENCES `payments` (`id`), - CONSTRAINT `fk_with` FOREIGN KEY (`with_id`) REFERENCES `users` (`id`) ON DELETE SET NULL, - CONSTRAINT `payments_account_id_foreign` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`), - CONSTRAINT `payments_invoice_id_foreign` FOREIGN KEY (`invoice_id`) REFERENCES `invoices` (`id`), - CONSTRAINT `payments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=30378 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `prs` --- - -DROP TABLE IF EXISTS `prs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `prs` ( - `text` text DEFAULT NULL, - `preimage` text DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `referrals` --- - -DROP TABLE IF EXISTS `referrals`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `referrals` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, - `sponsor_id` int(11) NOT NULL, - `token` varchar(255) NOT NULL, - `expiry` varchar(255) DEFAULT NULL, - `status` enum('available','used','expired','cancelled') NOT NULL DEFAULT 'available', - `created_at` datetime DEFAULT NULL, - `updated_at` datetime DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `referrals_user_id_foreign` (`user_id`), - KEY `referrals_sponsor_id_foreign` (`sponsor_id`), - CONSTRAINT `referrals_sponsor_id_foreign` FOREIGN KEY (`sponsor_id`) REFERENCES `users` (`id`), - CONSTRAINT `referrals_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `requests` --- - -DROP TABLE IF EXISTS `requests`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `requests` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `requester_id` int(11) DEFAULT NULL, - `recipient_id` int(11) DEFAULT NULL, - `memo` text DEFAULT NULL, - `createdAt` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), - `updatedAt` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', - `invoice_id` int(11) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `urls` --- - -DROP TABLE IF EXISTS `urls`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `urls` ( - `hash` varchar(255) DEFAULT NULL, - `apiKeyId` varchar(255) DEFAULT NULL, - `tag` varchar(255) DEFAULT NULL, - `params` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`params`)), - `initialUses` int(10) unsigned DEFAULT 1, - `remainingUses` int(10) unsigned DEFAULT 0, - `createdAt` timestamp NOT NULL DEFAULT current_timestamp(), - `updatedAt` timestamp NOT NULL DEFAULT current_timestamp(), - UNIQUE KEY `urls_hash_unique` (`hash`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `users` --- - -DROP TABLE IF EXISTS `users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `username` varchar(255) DEFAULT NULL, - `password` varchar(255) DEFAULT NULL, - `unit` varchar(255) DEFAULT 'SAT', - `currency` varchar(255) DEFAULT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `twofa` tinyint(1) DEFAULT NULL, - `pin` varchar(255) DEFAULT NULL, - `currencies` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, - `otpsecret` varchar(255) DEFAULT NULL, - `account_id` int(11) DEFAULT NULL, - `ip` int(10) unsigned DEFAULT NULL, - `subscriptions` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, - `seed` varchar(255) DEFAULT NULL, - `fiat` tinyint(1) NOT NULL DEFAULT 0, - `index` int(11) NOT NULL DEFAULT 0, - `verified` varchar(255) DEFAULT NULL, - `locked` tinyint(1) DEFAULT 0, - `authyId` varchar(255) DEFAULT NULL, - `admin` tinyint(1) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `phone` varchar(255) DEFAULT NULL, - `address` text DEFAULT NULL, - `profile` tinyint(1) DEFAULT 0, - `banner` tinyint(1) DEFAULT 0, - `theme` varchar(255) DEFAULT NULL, - `uuid` varchar(255) NOT NULL DEFAULT uuid(), - PRIMARY KEY (`id`), - KEY `ip` (`ip`), - KEY `username` (`username`) -) ENGINE=InnoDB AUTO_INCREMENT=15518 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `waiting_list` --- - -DROP TABLE IF EXISTS `waiting_list`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `waiting_list` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `email` varchar(255) NOT NULL, - `phone` varchar(255) NOT NULL, - `status` enum('pending','activated','expired','cancelled') NOT NULL, - `user_id` int(11) DEFAULT NULL, - `notes` text DEFAULT NULL, - `created_at` datetime DEFAULT NULL, - `updated_at` datetime DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `waiting_list_user_id_foreign` (`user_id`), - CONSTRAINT `waiting_list_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `withdrawals` --- - -DROP TABLE IF EXISTS `withdrawals`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `withdrawals` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `createdAt` datetime NOT NULL, - `updatedAt` datetime NOT NULL, - `amount` double DEFAULT NULL, - `completed` tinyint(1) DEFAULT NULL, - `transit` varchar(255) DEFAULT NULL, - `account` varchar(255) DEFAULT NULL, - `institution` varchar(255) DEFAULT NULL, - `notes` text DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `withdrawals_user_id_foreign` (`user_id`), - CONSTRAINT `withdrawals_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4119 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2022-11-24 19:44:06 diff --git a/doc/PAYMENTS.md b/doc/PAYMENTS.md deleted file mode 100644 index 2934aae8..00000000 --- a/doc/PAYMENTS.md +++ /dev/null @@ -1,52 +0,0 @@ -# This document describes the types of transactions and accounts that can occur in CoinOS - -## Receive bitcoin - -There are four ways a user can receive bitcoin - -- Bitcoin -- Liquid -- Lightning -- Internal - -In every case, a payment record is added to the `payments` table -The payment `type` will be one of BTC, LBTC, LNBTC, or COINOS -The `amount` will be a positive integer representing the number of satoshis received - -In the case of Bitcoin and Liquid payments, the `confirmed` column will be set to `false` initially until the transaction -is mined into a block on the blockchain. - -Also, the payment amount will be reflected in users' active account record in the `accounts` table under the `pending` column. The `balance` column will be updated when confirmations are received and the `pending` amount will be debited. - -## Send bitcoin - -Users can also send bitcoin over any of the four networks by pasting in or scanning a QR code of a bitcoin or liquid address, lightning invoice, or username. - -In the case of lightning, it's also possible to paste an [LNURL](https://github.com/fiatjaf/lnurl-rfc) which is a way of getting a lightning invoice from a remote server or other wallet over HTTP. - -If scanning or pasting an address or invoice that happens to belong to another coinos user, coinos will detect that and automatically change the payment type to an internal COINOS transfer as if you had typed in the recipient's username. - -## Creating Liquid Assets - -Users can issue new assets on the Liquid network at https://coinos.io/asset - -This will create an on-chain transaction that will issue a specified amount of new tokens. A new account record will be created in the `accounts` table for the user to track their balance of the new asset. As with receiving bitcoin over the Liquid network, the balance will be `pending` until one block confirmation has been received and then it will be updated. - -A separate payment record will be created to cover the fee for the issuance transaction, which is paid in L-BTC. - - -## Receiving Liquid Assets - -If the user has not received a particular asset before, a new account will be generated for them at the time the payment is detected. - -## Sending Liquid Assets - -Users can choose from a drop down menu to pick which account they want to be the active one. In the case of Liquid assets, they will only be able to an internal transfer to another coinos user, or send to a Liquid address. Users will receive an error if they paste in an LN invoice or on-chain BTC address while they have a Liquid asset accounted selected as their active account. - -## Depositing Fiat - -After having their account verified, users can fill out the deposit form at https://coinos.io/funding to indicate that they intend to send an interac or wire transfer. If the user is added to the list of accounts enabled for auto-deposit (under the `imap` config in config/index.js) then coinos will parse the Interac email it receives and automatically generate a new payment record and update the users Bitcoin account balance to credit them with an amount of bitcoin calculated equivalent to the fiat amount received, based on the current exchange rate less 2%. - -## Withdrawing Fiat - -Fiat withdrawals are currently processed manually. When a user requests to withdraw, we manually calculate how much bitcoin to remove from their account and do that with a database query before sending them an interac or wire transfer. diff --git a/doc/fund-server-bitcoin-cli.md b/doc/fund-server-bitcoin-cli.md deleted file mode 100644 index e0a2c5e3..00000000 --- a/doc/fund-server-bitcoin-cli.md +++ /dev/null @@ -1,44 +0,0 @@ -## Fund Coinos server (via bitcoin-cli) - -Bitcoin Regtest Mode allows us to create our own Bitcoin sandbox blockchain for use in testing & development. - - -From the coinos-server dir on your localfilesystem (ie- after following the 'Getting Started' section of our [root README]) - -```bash - -docker exec -it bitcoin bash - -# get an address -bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 getnewaddress -> bcrt1qzneawwyxe0fts5p8a6tsp4gu2mfnqzs92235hm # (example) - -# mine a block to the new address -bitcoin-cli -regtest -datadir=/home/bitcoin/.bitcoin generatetoaddress 101 theaddressgeneratedabove - -# verify you got the reward by doing: -bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 getbalance -> 50.00000000 - -# send 10 bitcoin to a coinos wallet account you created -# (get the address to send to from ./receive, select 'Bitcoin') -bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 sendtoaddress [account-address] 10 - -# confirm the transaction -# (do the mining command again) -bitcoin-cli -regtest -rpcuser=admin1 -rpcpassword=123 generatetoaddress 101 theoriginaladdressgeneratedabove -``` - -![](./img/50-bitcoin-server-balance.gif) - - - -#### further reading - -On why to generate 101 blocks and some general info on Regtest Mode: - -https://developer.bitcoin.org/examples/testing.html - - -[root README]:https://github.com/coinos/coinos-server - diff --git a/doc/img/50-bitcoin-server-balance.gif b/doc/img/50-bitcoin-server-balance.gif deleted file mode 100644 index 2fba5088..00000000 Binary files a/doc/img/50-bitcoin-server-balance.gif and /dev/null differ diff --git a/docker-compose.yml b/docker-compose.yml index d6117776..b4a73394 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,212 +1,41 @@ version: '3.6' services: - ui: - depends_on: - - app - container_name: ui - image: asoltys/coinos-ui:dev - ports: - - '8085:8085' - environment: - NODE_ENV: development - volumes: - - ../coinos-ui:/app - networks: - default: - ipv4_address: 172.18.0.27 app: - restart: always - depends_on: - - maria - - bitcoin - - liquid - - lnd - container_name: app image: cs + container_name: app + depends_on: + - rd + - bc + - cl build: . command: "pnpm dev" - ports: - - '3119:3119' environment: - NODE_OPTIONS: "--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js --openssl-legacy-provider" + NODE_OPTIONS: "--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js" NODE_ENV: development volumes: - ./:/app - ./logs:/logs - extra_hosts: - - host.docker.internal:host-gateway - networks: - default: - ipv4_address: 172.18.0.22 - bitcoin: + restart: always + bc: image: asoltys/bitcoin - container_name: bitcoin - ports: - - '18443:18443' + container_name: bc volumes: - ./config/bitcoin:/home/bitcoin/.bitcoin - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.23 - liquid: - image: ghcr.io/vulpemventures/elements - container_name: liquid - volumes: - - ./config/liquid:/home/elements/.elements - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.24 - lnd: - image: lightninglabs/lnd:v0.15.1-beta - container_name: lnd - volumes: - - ./config/lnd:/root/.lnd - - ./config/bitcoin:/root/.bitcoin - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.25 - clightning: + restart: always + cl: image: elementsproject/lightningd:latest container_name: cl environment: LIGHTNINGD_NETWORK: regtest volumes: - ./config/lightning:/root/.lightning - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.31 - lb: - image: elementsproject/lightningd:latest - container_name: lb - environment: - LIGHTNINGD_NETWORK: regtest - volumes: - - ./config/lb:/root/.lightning - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.36 - lc: - image: elementsproject/lightningd:latest - container_name: lc - environment: - LIGHTNINGD_NETWORK: regtest - volumes: - - ./config/lc:/root/.lightning - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.37 - maria: - restart: always - container_name: maria - image: mariadb:10.6.1 restart: always - environment: - MARIADB_ROOT_PASSWORD: ${ROOT_PASSWORD} - volumes: - - ./mysql:/var/lib/mysql - ports: - - 3307:3306 - networks: - default: - ipv4_address: 172.18.0.26 - electrsb: - depends_on: - - bitcoin - image: ghcr.io/vulpemventures/electrs - container_name: electrsb - entrypoint: - - /build/electrs - command: - - -vvvv - - --network - - regtest - - --daemon-dir - - /config - - --daemon-rpc-addr - - bitcoin:18443 - - --cookie - - admin1:123 - - --http-addr - - 0.0.0.0:3095 - - --electrum-rpc-addr - - 0.0.0.0:60400 - - --cors - - "*" - volumes: - - ./config/bitcoin/:/config - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.28 - electrs-liquid: - image: ghcr.io/vulpemventures/electrs-liquid - container_name: electrs - depends_on: - - liquid - entrypoint: - - /build/electrs - command: - - --jsonrpc-import - - -vvvv - - --parent-network - - regtest - - --network - - liquidregtest - - --daemon-dir - - /config - - --daemon-rpc-addr - - liquid:7040 - - --cookie - - admin1:123 - - --http-addr - - 0.0.0.0:3002 - - --electrum-rpc-addr - - 0.0.0.0:60401 - - --cors - - "*" - volumes: - - ./config/liquid/:/config - restart: unless-stopped - ports: - - '3012:3002' - networks: - default: - ipv4_address: 172.18.0.29 - esplora: - image: vulpemventures/esplora:latest - container_name: esplora - depends_on: - - electrs-liquid - environment: - API_URL: ${LIQUID_ELECTRS_URL} - NATIVE_ASSET_ID: 5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225 - IS_ELEMENTS: 1 - ports: - - ${LIQUID_ESPLORA_PORT}:5000 - restart: unless-stopped - networks: - default: - ipv4_address: 172.18.0.30 - redis: - container_name: redis + rd: image: redis + container_name: rd volumes: - ./config/redis:/data - eb: - container_name: eb - image: asoltys/electrs-batch - environment: - PORT: 7111 - ELECTRS_URL: http://electrs:3002 - CONCURRENCY: 10 - ports: - - '7111:7111' + restart: always nostr: container_name: nostr image: asoltys/nostr-rs-relay @@ -216,27 +45,4 @@ services: - "./config/nostr/data:/usr/src/app/db" - "./config/nostr/config.toml:/usr/src/app/config.toml" ports: - - '7001:8080' - networks: - default: - ipv4_address: 172.18.2.2 - api: - container_name: api - image: nostr-api - restart: always - build: ../nostr-api - command: "pnpm dev" - volumes: - - "../nostr-api:/app" - - "./config/nostr/data:/data" - environment: - DB: "/data/nostr.db" - ports: - - '9393:9393' - networks: - default: - ipv4_address: 172.18.2.4 -networks: - default: - name: net - external: true + - '8080:8080' diff --git a/fx.sample b/fx.sample deleted file mode 100644 index bdfbf9ea..00000000 --- a/fx.sample +++ /dev/null @@ -1 +0,0 @@ -{"fx":{"AED":3.6732,"AFN":76.916238,"ALL":102.317329,"AMD":481.616267,"ANG":1.794577,"AOA":655.221553,"ARS":81.969207,"AUD":1.343089,"AWG":1.8,"AZN":1.7025,"BAM":1.614261,"BBD":2,"BDT":84.775754,"BGN":1.617356,"BHD":0.377015,"BIF":1940.93179,"BMD":1,"BND":1.335611,"BOB":6.883324,"BRL":5.1698,"BSD":1,"BTC":0.000054,"BTN":73.603314,"BWP":10.890769,"BYN":2.553493,"BZD":2.015195,"CAD":1.28192,"CDF":1968.605194,"CHF":0.889253,"CLF":0.026934,"CLP":743.201103,"CNH":6.52695,"CNY":6.5432,"COP":3498.827763,"CRC":601.611868,"CUC":0.999757,"CUP":25.750002,"CVE":91.517508,"CZK":21.767701,"DJF":177.984204,"DKK":6.160477,"DOP":58.1879,"DZD":131.135347,"EGP":15.688401,"ERN":14.999681,"ETB":38.380578,"EUR":0.827603,"FJD":2.0594,"FKP":0.747215,"GBP":0.747215,"GEL":3.295,"GGP":0.747215,"GHS":5.865177,"GIP":0.747215,"GMD":51.775005,"GNF":9900.000797,"GTQ":7.803103,"GYD":209.079747,"HKD":7.75215,"HNL":24.277427,"HRK":6.247201,"HTG":67.978168,"HUF":295.195892,"IDR":14112.940636,"ILS":3.24973,"IMP":0.747215,"INR":73.753254,"IQD":1192.518145,"IRR":42105.00339,"ISK":127.030011,"JEP":0.747215,"JMD":143.995324,"JOD":0.709,"JPY":104.271008,"KES":111.400009,"KGS":84.795464,"KHR":4054.500326,"KMF":408.250238,"KPW":900.000073,"KRW":1085.489331,"KWD":0.304948,"KYD":0.833116,"KZT":420.239329,"LAK":9290.000748,"LBP":1519.000122,"LKR":185.75786,"LRD":160.450008,"LSL":14.960001,"LYD":1.35,"MAD":8.998358,"MDL":17.334117,"MGA":3867.375108,"MKD":51.003691,"MMK":1331.69855,"MNT":2855.764265,"MOP":7.98247,"MRO":357.000029,"MRU":37.121155,"MUR":39.903163,"MVR":15.455002,"MWK":765.388159,"MXN":19.864809,"MYR":4.0675,"MZN":74.150003,"NAD":14.960001,"NGN":381.321569,"NIO":34.83301,"NOK":8.836006,"NPR":117.76525,"NZD":1.423995,"OMR":0.385003,"PAB":1,"PEN":3.598707,"PGK":3.518051,"PHP":48.147447,"PKR":160.450013,"PLN":3.665672,"PYG":7034.254563,"QAR":3.64075,"RON":4.0275,"RSD":97.340008,"RUB":73.672606,"RWF":985.000079,"SAR":3.751408,"SBD":8.018641,"SCR":21.205532,"SDG":55.325005,"SEK":8.489209,"SGD":1.336996,"SHP":0.747215,"SLL":10194.893602,"SOS":584.000047,"SRD":14.154001,"SSP":130.26001,"STD":20900.545921,"STN":20.500001,"SVC":8.747973,"SYP":512.818375,"SZL":14.960001,"THB":30.045003,"TJS":11.324825,"TMT":3.51,"TND":2.7105,"TOP":2.288166,"TRY":7.823654,"TTD":6.793277,"TWD":28.256202,"TZS":2318.448187,"UAH":28.0933,"UGX":3680.09987,"USD":1,"UYU":42.677098,"UZS":10440.165624,"VEF":248487.662247,"VES":930536.054919,"VND":23187.74545,"VUV":111.531348,"WST":2.5692,"XAF":542.871898,"XAG":0.041832,"XAU":0.000545,"XCD":2.70255,"XDR":0.696164,"XOF":542.871898,"XPD":0.000439,"XPF":98.759293,"XPT":0.000995,"YER":250.350086,"ZAR":14.964601,"ZMW":21.038963,"ZWL":322.000026}} \ No newline at end of file diff --git a/git_hooks/pre-commit b/git_hooks/pre-commit deleted file mode 100644 index 78876285..00000000 --- a/git_hooks/pre-commit +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g') -[ -z "$FILES" ] && exit 0 - -# Prettify all selected files -echo "$FILES" | xargs ./node_modules/.bin/prettier --ignore-unknown --write - -# Add back the modified/prettified files to staging -echo "$FILES" | xargs git add - -exit 0 diff --git a/index.js b/index.js index e77ba2a8..b5ec0dca 100644 --- a/index.js +++ b/index.js @@ -1,52 +1,24 @@ import app from "./app"; -import persist from "./lib/persist"; -import config from "./config/index"; -import store from "./lib/store"; import { l, err } from "./lib/logging"; -import { optionalAuth } from "$lib/passport"; -import "./db"; import "./lib/redis"; import "./lib/sockets"; -import "./lib/webhooks"; import "./lib/passport"; import "./lib/upload"; -import "./lib/sync"; import "./lib/rates"; import "./lib/notifications"; import "./lib/mail"; import "./lib/nostr"; -import "./routes/assets"; -import "./routes/events"; import "./routes/locations"; -import "./routes/info"; -import "./routes/users"; import "./routes/invoices"; import "./routes/payments"; +import "./routes/users"; import "./routes/requests"; import "./routes/lnurl"; -import "./routes/tickets"; - -if (config.bitcoin) store.networks.push("bitcoin"); -if (config.liquid) store.networks.push("liquid"); -if (config.lna) store.networks.push("lightning"); let host = process.env.HOST || "0.0.0.0"; let port = process.env.PORT || 3119; - -app.post('/echo', optionalAuth, async (req, res) => { - console.log(req.user.username) - console.log(req.body) - res.send(req.body); -}); - -app.get('/nostr.json', async (req, res) => { - res.send({ - names: { adam: "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8", asoltys: "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8" } - }); -}); - app.listen({ host, port }, (e, address) => { e && err(e) && process.exit(1); l(`CoinOS Server listening on ${address}`); diff --git a/k8s/app-claim0-persistentvolumeclaim.yaml b/k8s/app-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index f4d8470e..00000000 --- a/k8s/app-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: app-claim0 - name: app-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/app-deployment.yaml b/k8s/app-deployment.yaml deleted file mode 100644 index 8dc491fa..00000000 --- a/k8s/app-deployment.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: app - name: app -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: app - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: app - spec: - containers: - - env: - - name: NODE_ENV - value: development - image: asoltys/coinos-server - name: app - ports: - - containerPort: 3119 - resources: {} - volumeMounts: - - mountPath: /app - name: app-claim0 - restartPolicy: Always - volumes: - - name: app-claim0 - persistentVolumeClaim: - claimName: app-claim0 -status: {} diff --git a/k8s/app-service.yaml b/k8s/app-service.yaml deleted file mode 100644 index 38a3b515..00000000 --- a/k8s/app-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: app - name: app -spec: - ports: - - name: "3119" - port: 3119 - targetPort: 3119 - selector: - io.kompose.service: app -status: - loadBalancer: {} diff --git a/k8s/bitcoin-claim0-persistentvolumeclaim.yaml b/k8s/bitcoin-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index 8ea5e7d0..00000000 --- a/k8s/bitcoin-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: bitcoin-claim0 - name: bitcoin-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/bitcoin-deployment.yaml b/k8s/bitcoin-deployment.yaml deleted file mode 100644 index 2192ece4..00000000 --- a/k8s/bitcoin-deployment.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: bitcoin - name: bitcoin -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: bitcoin - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: bitcoin - spec: - containers: - - image: asoltys/bitcoin:latest - name: bitcoin - ports: - - containerPort: 18442 - - containerPort: 18443 - resources: {} - volumeMounts: - - mountPath: /config - name: bitcoin-claim0 - restartPolicy: Always - volumes: - - name: bitcoin-claim0 - persistentVolumeClaim: - claimName: bitcoin-claim0 -status: {} diff --git a/k8s/bitcoin-service.yaml b/k8s/bitcoin-service.yaml deleted file mode 100644 index 1d6a6c12..00000000 --- a/k8s/bitcoin-service.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: bitcoin - name: bitcoin -spec: - ports: - - name: "18442" - port: 18442 - targetPort: 18442 - - name: "18443" - port: 18443 - targetPort: 18443 - selector: - io.kompose.service: bitcoin -status: - loadBalancer: {} diff --git a/k8s/docker-compose-resolved.yaml b/k8s/docker-compose-resolved.yaml deleted file mode 100644 index 8ec00d5e..00000000 --- a/k8s/docker-compose-resolved.yaml +++ /dev/null @@ -1,118 +0,0 @@ -services: - app: - container_name: app - environment: - NODE_ENV: development - image: asoltys/coinos-server - ports: - - published: 3119 - target: 3119 - volumes: - - /home/adam/coinos-server:/app:rw - bitcoin: - container_name: bitcoin - image: asoltys/bitcoin:latest - ports: - - published: 18442 - target: 18442 - - published: 18443 - target: 18443 - restart: unless-stopped - volumes: - - /home/adam/coinos-server/config/bitcoin:/config:rw - electrs-liquid: - command: - - -vvvv - - --parent-network - - regtest - - --network - - liquidregtest - - --daemon-dir - - /config - - --daemon-rpc-addr - - liquid:7040 - - --cookie - - admin1:123 - - --http-addr - - 0.0.0.0:3002 - - --electrum-rpc-addr - - 0.0.0.0:60401 - - --cors - - '*' - container_name: electrs-liquid - entrypoint: - - /build/electrs - image: vulpemventures/electrs-liquid:latest - ports: - - published: 3013 - target: 3002 - - published: 60402 - target: 60401 - restart: unless-stopped - volumes: - - /home/adam/coinos-server/liquid:/config:rw - esplora-liquid: - container_name: esplora-liquid - environment: - API_URL: http://localhost:3012 - IS_ELEMENTS: 1 - NATIVE_ASSET_ID: 5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225 - image: vulpemventures/esplora:latest - ports: - - published: 5006 - target: 5000 - restart: unless-stopped - la: - container_name: la - environment: - LIGHTNINGD_NETWORK: regtest - image: elementsproject/lightningd:latest - restart: unless-stopped - volumes: - - /home/adam/coinos-server/config/lightning:/root/.lightning:rw - lb: - container_name: lb - environment: - LIGHTNINGD_NETWORK: regtest - image: elementsproject/lightningd:latest - restart: unless-stopped - volumes: - - /home/adam/coinos-server/config/lightningb:/root/.lightning:rw - liquid: - container_name: liquid - image: asoltys/liquid:latest - ports: - - published: 7040 - target: 7040 - - published: 18444 - target: 18444 - - published: 18602 - target: 18602 - - published: 18603 - target: 18603 - restart: unless-stopped - volumes: - - /home/adam/coinos-server/config/liquid:/config:rw - maria: - container_name: mariadb - environment: - MARIADB_ROOT_PASSWORD: password - image: mariadb:10.6.1 - ports: - - published: 3307 - target: 3306 - restart: always - volumes: - - /home/adam/coinos-server/mysql:/var/lib/mysql:rw - ui: - container_name: ui - environment: - NODE_ENV: development - image: asoltys/coinos-ui - ports: - - published: 8085 - target: 8085 - volumes: - - /home/adam/coinos-ui:/coinos:rw -version: '3.6' - diff --git a/k8s/electrs-liquid-claim0-persistentvolumeclaim.yaml b/k8s/electrs-liquid-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index 43109416..00000000 --- a/k8s/electrs-liquid-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: electrs-liquid-claim0 - name: electrs-liquid-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/electrs-liquid-deployment.yaml b/k8s/electrs-liquid-deployment.yaml deleted file mode 100644 index e33098f2..00000000 --- a/k8s/electrs-liquid-deployment.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: electrs-liquid - name: electrs-liquid -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: electrs-liquid - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: electrs-liquid - spec: - containers: - - args: - - -vvvv - - --parent-network - - regtest - - --network - - liquidregtest - - --daemon-dir - - /config - - --daemon-rpc-addr - - liquid:7040 - - --cookie - - admin1:123 - - --http-addr - - 0.0.0.0:3002 - - --electrum-rpc-addr - - 0.0.0.0:60401 - - --cors - - '*' - command: - - /build/electrs - image: vulpemventures/electrs-liquid:latest - name: electrs-liquid - ports: - - containerPort: 3002 - - containerPort: 60401 - resources: {} - volumeMounts: - - mountPath: /config - name: electrs-liquid-claim0 - restartPolicy: Always - volumes: - - name: electrs-liquid-claim0 - persistentVolumeClaim: - claimName: electrs-liquid-claim0 -status: {} diff --git a/k8s/electrs-liquid-service.yaml b/k8s/electrs-liquid-service.yaml deleted file mode 100644 index 83e57bd1..00000000 --- a/k8s/electrs-liquid-service.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: electrs-liquid - name: electrs-liquid -spec: - ports: - - name: "3013" - port: 3013 - targetPort: 3002 - - name: "60402" - port: 60402 - targetPort: 60401 - selector: - io.kompose.service: electrs-liquid -status: - loadBalancer: {} diff --git a/k8s/esplora-liquid-deployment.yaml b/k8s/esplora-liquid-deployment.yaml deleted file mode 100644 index 4606a416..00000000 --- a/k8s/esplora-liquid-deployment.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: esplora-liquid - name: esplora-liquid -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: esplora-liquid - strategy: {} - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: esplora-liquid - spec: - containers: - - env: - - name: API_URL - value: http://localhost:3012 - - name: IS_ELEMENTS - value: "1" - - name: NATIVE_ASSET_ID - value: 5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225 - image: vulpemventures/esplora:latest - name: esplora-liquid - ports: - - containerPort: 5000 - resources: {} - restartPolicy: Always -status: {} diff --git a/k8s/esplora-liquid-service.yaml b/k8s/esplora-liquid-service.yaml deleted file mode 100644 index 17875690..00000000 --- a/k8s/esplora-liquid-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: esplora-liquid - name: esplora-liquid -spec: - ports: - - name: "5006" - port: 5006 - targetPort: 5000 - selector: - io.kompose.service: esplora-liquid -status: - loadBalancer: {} diff --git a/k8s/la-claim0-persistentvolumeclaim.yaml b/k8s/la-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index d8c062a8..00000000 --- a/k8s/la-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: la-claim0 - name: la-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/la-deployment.yaml b/k8s/la-deployment.yaml deleted file mode 100644 index a42eee8c..00000000 --- a/k8s/la-deployment.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: la - name: la -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: la - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: la - spec: - containers: - - env: - - name: LIGHTNINGD_NETWORK - value: regtest - image: elementsproject/lightningd:latest - name: la - resources: {} - volumeMounts: - - mountPath: /root/.lightning - name: la-claim0 - restartPolicy: Always - volumes: - - name: la-claim0 - persistentVolumeClaim: - claimName: la-claim0 -status: {} diff --git a/k8s/lb-claim0-persistentvolumeclaim.yaml b/k8s/lb-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index 2e77148f..00000000 --- a/k8s/lb-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: lb-claim0 - name: lb-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/lb-deployment.yaml b/k8s/lb-deployment.yaml deleted file mode 100644 index 064df112..00000000 --- a/k8s/lb-deployment.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: lb - name: lb -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: lb - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: lb - spec: - containers: - - env: - - name: LIGHTNINGD_NETWORK - value: regtest - image: elementsproject/lightningd:latest - name: lb - resources: {} - volumeMounts: - - mountPath: /root/.lightning - name: lb-claim0 - restartPolicy: Always - volumes: - - name: lb-claim0 - persistentVolumeClaim: - claimName: lb-claim0 -status: {} diff --git a/k8s/liquid-claim0-persistentvolumeclaim.yaml b/k8s/liquid-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index 2d9030df..00000000 --- a/k8s/liquid-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: liquid-claim0 - name: liquid-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/liquid-deployment.yaml b/k8s/liquid-deployment.yaml deleted file mode 100644 index 006a06e6..00000000 --- a/k8s/liquid-deployment.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: liquid - name: liquid -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: liquid - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: liquid - spec: - containers: - - image: asoltys/liquid:latest - name: liquid - ports: - - containerPort: 7040 - - containerPort: 18444 - - containerPort: 18602 - - containerPort: 18603 - resources: {} - volumeMounts: - - mountPath: /config - name: liquid-claim0 - restartPolicy: Always - volumes: - - name: liquid-claim0 - persistentVolumeClaim: - claimName: liquid-claim0 -status: {} diff --git a/k8s/liquid-service.yaml b/k8s/liquid-service.yaml deleted file mode 100644 index d3b56800..00000000 --- a/k8s/liquid-service.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: liquid - name: liquid -spec: - ports: - - name: "7040" - port: 7040 - targetPort: 7040 - - name: "18444" - port: 18444 - targetPort: 18444 - - name: "18602" - port: 18602 - targetPort: 18602 - - name: "18603" - port: 18603 - targetPort: 18603 - selector: - io.kompose.service: liquid -status: - loadBalancer: {} diff --git a/k8s/maria-claim0-persistentvolumeclaim.yaml b/k8s/maria-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index 55503cdb..00000000 --- a/k8s/maria-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: maria-claim0 - name: maria-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/maria-deployment.yaml b/k8s/maria-deployment.yaml deleted file mode 100644 index 75d6153f..00000000 --- a/k8s/maria-deployment.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: maria - name: maria -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: maria - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: maria - spec: - containers: - - env: - - name: MARIADB_ROOT_PASSWORD - value: password - image: mariadb:10.6.1 - name: mariadb - ports: - - containerPort: 3306 - resources: {} - volumeMounts: - - mountPath: /var/lib/mysql - name: maria-claim0 - restartPolicy: Always - volumes: - - name: maria-claim0 - persistentVolumeClaim: - claimName: maria-claim0 -status: {} diff --git a/k8s/maria-service.yaml b/k8s/maria-service.yaml deleted file mode 100644 index 31789fe1..00000000 --- a/k8s/maria-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: maria - name: maria -spec: - ports: - - name: "3307" - port: 3307 - targetPort: 3306 - selector: - io.kompose.service: maria -status: - loadBalancer: {} diff --git a/k8s/ui-claim0-persistentvolumeclaim.yaml b/k8s/ui-claim0-persistentvolumeclaim.yaml deleted file mode 100644 index aafbd9b4..00000000 --- a/k8s/ui-claim0-persistentvolumeclaim.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - creationTimestamp: null - labels: - io.kompose.service: ui-claim0 - name: ui-claim0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Mi -status: {} diff --git a/k8s/ui-deployment.yaml b/k8s/ui-deployment.yaml deleted file mode 100644 index 86912390..00000000 --- a/k8s/ui-deployment.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: ui - name: ui -spec: - replicas: 1 - selector: - matchLabels: - io.kompose.service: ui - strategy: - type: Recreate - template: - metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: ui - spec: - containers: - - env: - - name: NODE_ENV - value: development - image: asoltys/coinos-ui - name: ui - ports: - - containerPort: 8085 - resources: {} - volumeMounts: - - mountPath: /coinos - name: ui-claim0 - restartPolicy: Always - volumes: - - name: ui-claim0 - persistentVolumeClaim: - claimName: ui-claim0 -status: {} diff --git a/k8s/ui-service.yaml b/k8s/ui-service.yaml deleted file mode 100644 index 21019ff6..00000000 --- a/k8s/ui-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - annotations: - kompose.cmd: kompose convert -f docker-compose-resolved.yaml - kompose.version: 1.22.0 (955b78124) - creationTimestamp: null - labels: - io.kompose.service: ui - name: ui -spec: - ports: - - name: "8085" - port: 8085 - targetPort: 8085 - selector: - io.kompose.service: ui -status: - loadBalancer: {} diff --git a/lib/account.js b/lib/account.js deleted file mode 100644 index 1576546a..00000000 --- a/lib/account.js +++ /dev/null @@ -1,49 +0,0 @@ -import axios from "axios"; -import db from "$db"; -import store from "$lib/store"; - -export default async (asset, user, transaction) => { - const account = await db.Account.findOne({ - where: { - user_id: user.id, - asset, - pubkey: null - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (account) return account; - - let params = { - user_id: user.id, - asset - }; - let name = asset.substr(0, 6); - let domain = ""; - let ticker = asset.substr(0, 3).toUpperCase(); - let precision = 8; - - if (store.assets[asset]) { - ({ domain, ticker, precision, name } = store.assets[asset]); - } else { - const existing = await db.Account.findOne({ - where: { - asset - }, - order: [["id", "ASC"]], - limit: 1, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (existing) { - ({ domain, ticker, precision, name } = existing); - } - } - - params = { ...params, ...{ domain, ticker, precision, name } }; - params.balance = 0; - params.pending = 0; - return db.Account.create(params, { transaction }); -}; diff --git a/lib/buildliquidtx.js b/lib/buildliquidtx.js deleted file mode 100644 index 4aad18f4..00000000 --- a/lib/buildliquidtx.js +++ /dev/null @@ -1,160 +0,0 @@ -import config from "$config"; -import db from "$db"; -import store from "./store"; -import { prod } from "./utils"; -import axios from "axios"; -import liquid from "liquidjs-lib"; -import coinselect from "coinselect"; -import split from "coinselect/split"; -const api = prod - ? "https://blockstream.info/liquid/api" - : "http://localhost:3002"; -const SATS = 100000000; -const network = prod ? liquid.networks["liquid"] : liquid.networks["regtest"]; -import { Op } from "@sequelize/core"; - -export default async ({ - address, - asset, - amount, - feeRate, - replaceable, - user -}) => { - const { btcasset: btc } = config.liquid; - - if (!feeRate) { - let { feerate } = await lq.estimateSmartFee(6); - if (!feerate) feerate = 0.00002; - feeRate = feerate * SATS; - } - feeRate = Math.round(feeRate / 100); - if (asset !== btc) feeRate = 0; - - let payments; - if (asset === btc) { - payments = await db.Payment.findAll({ - where: { - received: true, - account_id: user.account_id, - network: "liquid" - } - }); - } else { - payments = await db.Payment.findAll({ - where: { - "$account.pubkey": { [Op.ne]: null }, - received: true, - user_id: user.id, - network: "liquid" - }, - include: { - model: db.Account, - as: "account" - } - }); - } - - let utxos = []; - for (let i = 0; i < payments.length; i++) { - let url = `${api}/address/${payments[i].address}/utxo`; - try { - utxos.push(...(await axios.get(url)).data); - } catch (e) { - err("problem fetching utxos", e.message, url); - } - } - - let feeInput = utxos.find(utxo => utxo.asset === btc && utxo.value > 1000); - if (!feeInput) - throw new Error( - "Insufficient balance, couldn't find an input to cover the fee" - ); - - utxos = utxos.filter(utxo => utxo.asset === asset); - - let feeChange = feeInput.value - 1000; - - let targets = [ - { - address, - value: amount - } - ]; - - let { inputs, outputs, fee } = coinselect(utxos, targets, feeRate); - if (!inputs || !outputs) { - l( - "utxos", - amount, - feeRate, - utxos.map(u => u.value), - inputs, - outputs, - fee - ); - - throw new Error( - "Couldn't find inputs for transaction, check your balance/fee" - ); - } - - let psbt = new liquid.Psbt({ network }); - let total = 0; - - let { address: changeAddress } = payments[payments.length - 1]; - - outputs.map(({ address, value }) => { - total += value; - if (!address) { - address = changeAddress; - store.change.push(address); - } - - psbt = psbt.addOutput({ - address, - asset, - value - }); - }); - - for (let i = 0; i < inputs.length; i++) { - let { txid, vout } = inputs[i]; - psbt = psbt.addInput({ - hash: txid, - index: vout, - nonWitnessUtxo: Buffer.from((await lq.getTransaction(txid)).hex, "hex") - }); - } - - if (asset !== btc) { - psbt.addInput({ - hash: feeInput.txid, - index: feeInput.vout, - nonWitnessUtxo: Buffer.from( - await lq.getTransaction(feeInput.txid).hex, - "hex" - ) - }); - - psbt.addOutput({ - changeAddress, - asset: btc, - value: feeChange - }); - } - - psbt.addOutput({ - asset: btc, - script: Buffer.alloc(0), - value: fee, - nonce: Buffer.alloc(1, 0) - }); - - if (replaceable) psbt.setInputSequence(0, 0xffffffff - 2); - - feeRate = Math.round(feeRate * 1000); - psbt = psbt.toBase64(); - - return { tx: psbt, feeRate }; -}; diff --git a/lib/buildtx.js b/lib/buildtx.js deleted file mode 100644 index daed2e73..00000000 --- a/lib/buildtx.js +++ /dev/null @@ -1,90 +0,0 @@ -import store from "./store"; -import { prod } from "./utils"; -import axios from "axios"; -import bitcoin from "bitcoinjs-lib"; -import coinselect from "coinselect"; -import split from "coinselect/split"; -const api = prod ? "https://blockstream.info/api" : "http://electrsb:3095"; -const SATS = 100000000; -const network = prod - ? bitcoin.networks["bitcoin"] - : bitcoin.networks["regtest"]; - -export default async ({ address, amount, feeRate, replaceable, user }) => { - if (!feeRate) feeRate = (await bc.estimateSmartFee(6)).feerate * SATS; - feeRate = Math.round(feeRate / 1000); - - const payments = await db.Payment.findAll({ - where: { - received: true, - account_id: user.account_id, - network: "bitcoin" - } - }); - - const addresses = [...new Set(payments.map(p => p.address))]; - const utxos = []; - for (let i = 0; i < addresses.length; i++) { - try { - utxos.push( - ...(await axios.get(`${api}/address/${addresses[i]}/utxo`)).data - ); - } catch (e) { - err("problem fetching utxos", e.message); - } - } - - let targets = [ - { - address, - value: amount - } - ]; - - let { inputs, outputs, fee } = coinselect(utxos, targets, feeRate); - - if (!inputs || !outputs) { - l( - "utxos", - amount, - feeRate, - utxos.map(u => u.value) - ); - throw new Error( - "Couldn't find inputs for transaction, check your balance/fee" - ); - } - - let psbt = new bitcoin.Psbt({ network }); - let total = 0; - - outputs.map(({ address, value }) => { - total += value; - if (!address) { - address = payments[payments.length - 1].address; - store.change.push(address); - } - - psbt = psbt.addOutput({ - address, - value - }); - }); - - for (let i = 0; i < inputs.length; i++) { - let { txid, vout } = inputs[i]; - let nonWitnessUtxo = Buffer.from(await bc.getRawTransaction(txid), "hex"); - psbt = psbt.addInput({ - hash: txid, - index: vout, - nonWitnessUtxo - }); - } - - if (replaceable) psbt.setInputSequence(0, 0xffffffff - 2); - - feeRate = Math.round(feeRate * 1000); - psbt = psbt.toBase64(); - - return { tx: psbt, feeRate }; -}; diff --git a/lib/countries.js b/lib/countries.js deleted file mode 100644 index 4d5a48dc..00000000 --- a/lib/countries.js +++ /dev/null @@ -1,249 +0,0 @@ -export default { - AF: "AFG", - AL: "ALB", - DZ: "DZA", - AD: "AND", - AO: "AGO", - AI: "AIA", - AQ: "ATA", - AR: "ARG", - AM: "ARM", - AW: "ABW", - AU: "AUS", - AT: "AUT", - AZ: "AZE", - BS: "BHS", - BH: "BHR", - BD: "BGD", - BB: "BRB", - BY: "BLR", - BE: "BEL", - BZ: "BLZ", - BJ: "BEN", - BM: "BMU", - BT: "BTN", - BO: "BOL", - BW: "BWA", - BV: "NOK", - BR: "BRA", - IO: "USD", - VG: "USD", - BN: "BRN", - BG: "BGR", - BF: "XOF", - BI: "BDI", - KH: "KHM", - CM: "CMR", - CA: "CAN", - IC: "EUR", - CV: "CVE", - KY: "KYD", - CF: "XAF", - TD: "TCD", - CL: "CHL", - CN: "CHN", - CX: "AUD", - CC: "AUD", - CO: "COL", - KM: "COM", - CG: "XAF", - CD: "CDF", - CK: "NZD", - CR: "CRC", - CI: "XOF", - HR: "HRV", - CU: "CUB", - CW: "ANG", - CY: "CYP", - CZ: "CZK", - DK: "DNK", - DJ: "DJI", - DM: "DMA", - DO: "DOP", - TL: "IDS", - EC: "ECU", - EG: "EGY", - SV: "SVC", - GQ: "XAF", - ER: "ERI", - EE: "EST", - ET: "ETH", - FK: "FKP", - FO: "DKK", - FJ: "FJI", - FI: "FIN", - FR: "FRA", - GF: "EUR", - PF: "XPF", - TF: "EUR", - GA: "GAB", - GM: "GMB", - GE: "GEO", - DE: "DEU", - GH: "GHA", - GI: "GIB", - GR: "GRC", - GL: "GRL", - GD: "GRD", - GP: "GLP", - GU: "GUM", - GT: "GTM", - GG: "GGY", - GN: "GIN", - GW: "XOF", - GY: "GUY", - HT: "HTI", - HM: "AUD", - HN: "HND", - HK: "HKD", - HU: "HUN", - IS: "ISL", - IN: "IND", - ID: "IDN", - IR: "IRN", - IQ: "IRQ", - IE: "IRL", - Of: "Man", - IL: "ISR", - IT: "ITA", - JM: "JAM", - JP: "JPN", - JE: "JEY", - JO: "JOR", - KZ: "KAZ", - KE: "KEN", - KI: "KIR", - XK: "XKX", - KW: "KWT", - KG: "KGZ", - LA: "LAO", - LV: "LVA", - LB: "LBN", - LS: "LSO", - LR: "LBR", - LY: "LBY", - LI: "LIE", - LT: "LTU", - LU: "LUX", - MO: "MAC", - MK: "MKD", - MG: "MDG", - MW: "MWI", - MY: "MYS", - MV: "MDV", - ML: "MLI", - MT: "MLT", - MH: "USD", - MQ: "MTQ", - MR: "MRT", - MU: "MUS", - YT: "MYT", - MX: "MEX", - FM: "FSM", - MD: "MDA", - MC: "MCO", - MN: "MNG", - ME: "MNE", - MS: "MSR", - MA: "MAR", - MZ: "MOZ", - MM: "MMR", - NA: "NAM", - NR: "NRU", - NP: "NPL", - NL: "NLD", - AN: "ANG", - NC: "XPF", - NZ: "NZD", - NI: "NIC", - NE: "NER", - NG: "NGA", - NU: "NIU", - NF: "AUD", - KP: "KPW", - MP: "USD", - NO: "NOR", - OM: "OMN", - PK: "PAK", - PW: "PLW", - PS: "PSE", - PA: "PAN", - PG: "PGK", - PY: "PRY", - PE: "PER", - PH: "PHL", - PN: "PCN", - PL: "POL", - PT: "PRT", - PR: "USD", - QA: "QAT", - RE: "REU", - RO: "ROU", - RU: "RUS", - RW: "RWA", - BL: "EUR", - SH: "GPB", - KN: "XCD", - LC: "XCD", - MF: "EUR", - PM: "EUR", - VC: "XCD", - WS: "WSM", - SM: "EUR", - ST: "STN", - SA: "SAR", - SN: "SEN", - RS: "SRB", - CS: "RSD", - SC: "SYC", - SL: "SLL", - SG: "SGP", - SX: "ANG", - SK: "SVK", - SI: "SVN", - SB: "SBD", - SO: "SOM", - ZA: "ZAR", - GS: "GBP", - KR: "KRW", - SS: "SSP", - ES: "ESP", - LK: "LKR", - SD: "SDN", - SR: "SUR", - SJ: "NOK", - SZ: "SWZ", - SE: "SWE", - CH: "CHE", - SY: "SYR", - TW: "TWN", - TJ: "TJK", - TZ: "TZA", - TH: "THA", - TG: "TGO", - TK: "TKL", - TO: "TON", - TT: "TTD", - TA: "SHP", - TN: "TUN", - TR: "TUR", - TM: "TKM", - TC: "USD", - TV: "TUV", - UM: "USD", - VI: "USD", - UG: "UGA", - UA: "UKR", - AE: "AED", - GB: "GBP", - US: "USD", - UY: "URY", - UZ: "UZB", - VU: "VUT", - VA: "VAT", - VE: "VEN", - VN: "VNM", - YE: "YEM", - ZM: "ZMB", - ZW: "ZWE" -}; diff --git a/lib/liquid.js b/lib/liquid.js deleted file mode 100644 index ccf6aafb..00000000 --- a/lib/liquid.js +++ /dev/null @@ -1,3 +0,0 @@ -import config from "$config"; -import rpc from "./rpc"; -export default rpc(config.liquid); diff --git a/lib/lnd.js b/lib/lnd.js deleted file mode 100644 index ff8b4c56..00000000 --- a/lib/lnd.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/lib/logo.js b/lib/logo.js deleted file mode 100644 index 922716ac..00000000 --- a/lib/logo.js +++ /dev/null @@ -1 +0,0 @@ -export default "iVBORw0KGgoAAAANSUhEUgAABbYAAAW2CAYAAABXn/ThAAAgAElEQVR4Xuza2W7d5bnA4c/OyLQRUxg2IEoACaG2opV6F0jcTg/KCRfAQS+qJ5VQq1Y9aCuKxFyGTUMgk5Ot729WmrJ35WQ18Vo/+4nkOkm9sl4/7xs7/pmdMcaN4QcBAgQIECBAgAABAgQIECBAgAABAgQIEIgI7AjbkU0ZkwABAgQIECBAgAABAgQIECBAgAABAgQWAWHbIRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIEDNS+v0AACAASURBVCBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIECBAgAABAgQIECBAgACBlICwnVqXYQkQIECAAAECBAgQIECAAAECBAgQIEBA2HYDBAgQIECAAAECBAgQIECAAAECBAgQIJASELZT6zIsAQIECBAgQIAAAQIECBAgQIAAAQIECAjbboAAAQIECBAgQIAAAQIECBAgQIAAAQIEUgLCdmpdhiVAgAABAgQIECBAgAABAgQIECBAgAABYdsNECBAgAABAgQIECBAgAABAgQIECBAgEBKQNhOrcuwBAgQIECAAAECBAgQIECAAAECBAgQICBsuwECBAgQIECAAAECBAgQIECAAAECBAgQSAkI26l1GZYAAQIECBAgQIAAAQIECBAgQIAAAQIEhG03QIAAAQIECBAgQIAAAQIECBAgQIAAAQIpAWE7tS7DEiBAgAABAgQIECBAgAABAgQIECBAgICw7QYIECBAgAABAgQIECBAgAABAgQIECBAICUgbKfWZVgCBAgQIECAAAECBAgQIECAAAECBAgQELbdAAECBAgQIECAAAECBAgQIECAAAECBAikBITt1LoMS4AAAQIECBAgQIAAAQIECBAgQIAAAQLCthsgQIAAAQIECBAgQIAAAQIECBAgQIAAgZSAsJ1al2EJECBAgAABAgQIECBAgAABAgQIECBAQNh2AwQIECBAgAABAgQIECBAgAABAgQIECCQEhC2U+syLAECBAgQIECAAAECBAgQIECAAAECBAgI226AAAECBAgQIECAAAECBAgQIECAAAECBFICwnZqXYYlQIAAAQIECBAgQIAAAQIECBAgQIAAAWHbDRAgQIAAAQIECBAgQIAAAQIECBAgQIBASkDYTq3LsAQIECBAgAABAgQIECBAgAABAgQIECAgbLsBAgQIECBAgAABAgQIECBAgAABAgQIEEgJCNupdRmWAAECBAgQIECAAAECBAgQIECAAAECBIRtN0CAAAECBAgQIECAAAECBAgQIECAAAECKQFhO7UuwxIgQIAAAQIECBAgQIAAAQIECBAgQICAsO0GCBAgQIAAAQIECBAgQIAAAQIECBAgQCAlIGyn1mVYAgQIECBAgAABAgQIECBAgAABzP6fVgAAIABJREFUAgQIEBC23QABAgQIECBAgAABAgQIECBAgAABAgQIpASE7dS6DEuAAAECBAgQIECAAAECBAgQIECAAAECwrYbIECAAAECBAgQIECAAAECBAgQIECAAIGUgLCdWpdhCRAgQIAAAQIECBAgQIAAAQIECBAgQEDYdgMECBAgQIAAAQIECBAgQIAAAQIECBAgkBIQtlPrMiwBAgQIECBAgAABAgQIECBAgAABAgQICNtugAABAgQIECBAgAABAgQIECBAgAABAgRSAsJ2al2GJUCAAAECBAgQIECAAAECBAgQIECAAAFh2w0QIECAAAECBAgQIECAAAECBAgQIECAQEpA2E6ty7AECBAgQIAAAQIECBAgQIAAAQIECBAgIGy7AQIECBAgQIAAAQIECBAgQIAAAQIECBBICQjbqXUZlgABAgQIECBAgAABAgQIECBAgAABAgSEbTdAgAABAgQIECBAgAABAgQIECBAgAABAikBYTu1LsMSIECAAAECBAgQIECAAAECBAgQIECAgLDtBggQIECAAAECBAgQIECAAAECBAgQIEAgJSBsp9ZlWAIECBAgQIAAAQIECBAgQIAAAQIECBAQtt0AAQIECBAgQIAAAQIECBAgQIAAAQIECKQEhO3UugxLgAABAgQIECBAgAABAgQIECBAgAABAsK2GyBAgAABAgQIbJHAm2++Od56663x+uuvH+pUv/rVr8Y777wzLl68eKjP68kIECBAgAABAgQIECCwjoCwvY6axxAgQIAAAQIE7pGAsH2PYP2xBAgQIECAAAECBAgcKQFh+0it0ztDgAABAgQI1AWE7foGzU+AAAECBAgQIECAwGEICNuHoew5CBAgQIAAAQK3KSBs3yaUNyNAgAABAgQIECBA4FgLCNvHev3eeQIECBAgQGDbBITtbduIeQgQIECAAAECBAgQ2EYBYXsbt2ImAgQIECBA4NgKCNvHdvXecQIECBAgQIAAAQIE7kBA2L4DLG9KgAABAgQIELjXAsL2vRb25xMgQIAAAQIECBAgcBQEhO2jsEXvAwECBAgQIHBkBITtI7NK7wgBAgQIECBAgAABAvdQQNi+h7j+aAIECBAgQIDAnQoI23cq5u0JECBAgAABAgQIEDiOAsL2cdy695kAAQIECBDYWgFhe2tXYzACBAgQIECAAAECBLZIQNjeomUYhQABAgQIECAgbLsBAgQIECBAgAABAgQIHCwgbB9s5C0IECBAgAABAocmIGwfGrUnIkCAAAECBAgQIEAgLCBsh5dndAIECBAgQODoCQjbR2+n3iMCBAgQIECAAAECBO6+gLB99039iQQIECBAgACBtQWE7bXpPJAAAQIECBAgQIAAgWMkIGwfo2V7VwkQIECAAIHtFxC2t39HJiRAgAABAgQIECBAYPMCwvbmd2ACAgQIECBAgMBNAWHbMRAgQIAAAQIECBAgQOBgAWH7YCNvQYAAAQIECBA4NAFh+9CoPREBAgQIECBAgAABAmEBYTu8PKMTIECAAAECR09A2D56O/UeESBAgAABAgQIECBw9wWE7btv6k8kQIAAAQIECKwtIGyvTeeBBAgQIECAAAECBAgcIwFh+xgt27tKgAABAgQIbL+AsL39OzIhAQIECBAgQIAAAQKbFxC2N78DExAgQIAAAQIEbgoI246BAAECBAgQIECAAAECBwsI2wcbeQsCBAgQIECAwKEJCNuHRu2JCBAgQIAAAQIECBAICwjb4eUZnQABAgQIEDh6AsL20dup94gAAQIECBAgQIAAgbsvIGzffVN/IgECBAgQIEBgbQFhe206DyRAgAABAgQIECBA4BgJCNvHaNneVQIECBAgQGD7BYTt7d+RCQkQIECAAAECBAgQ2LyAsL35HZiAAAECBAgQIHBTQNh2DAQIECBAgAABAgQIEDhYQNg+2MhbECBAgAABAgQOTUDYPjRqT0SAAAECBAgQIECAQFhA2A4vz+gECBAgQIDA0RMQto/eTr1HBAgQIECAAAECBAjcfQFh++6b+hMJECBAgAABAmsLCNtr03kgAQIECBAgQIAAAQLHSEDYPkbL9q4SIECAAAEC2y8gbG//jkxIgAABAgQIECBAgMDmBYTtze/ABAQIECBAgACBmwLCtmMgQIAAAQIECBAgQIDAwQLC9sFG3oIAAQIECBAgcGgCwvahUXsiAgQIECBAgAABAgTCAsJ2eHlGJ0CAAAECBI6egLB99HbqPSJAgAABAgQIECBA4O4LCNt339SfSIAAAQIECBBYW0DYXpvOAwkQIECAAAECBAgQOEYCwvYxWrZ3lQABAgQIENh+AWF7+3dkQgIECBAgQIAAAQIENi8gbG9+ByYgQIAAAQIECNwUELYdAwECBAgQIECAAAECBA4WELYPNvIWBAgQIECAAIFDE3jjjTfGL3/5y/HTn/700J5zPtHbb789fv3rX4+LFy8e6vN6MgIECBAgQIAAAQIECKwjIGyvo+YxBAgQIECAAIF7JHD+/Pnxi1/8Ypw7d+4ePcP//8f+5je/Ge++++64evXqoT6vJyNAgAABAgQIECBAgMA6AsL2OmoeQ4AAAQIECBAgQIAAAQIECBAgQIAAAQIbExC2N0bviQkQIECAAAECBAgQIECAAAECBAgQIEBgHQFhex01jyFAgAABAgQIECBAgAABAgQIECBAgACBjQkI2xuj98QECBAgQIAAAQIECBAgQIAAAQIECBAgsI6AsL2OmscQIECAAAECBAgQIECAAAECBAgQIECAwMYEhO2N0XtiAgQIECBAgAABAgQIECBAgAABAgQIEFhHQNheR81jCBAgQIAAAQIECBAgQIAAAQIECBAgQGBjAsL2xug9MQECBAgQIECAAAECBAgQIECAAAECBAisIyBsr6PmMQQIECBAgAABAgQIECBAgAABAgQIECCwMQFhe2P0npgAAQIECBAgQIAAAQIECBAgQIAAAQIE1hEQttdR8xgCBAgQIECAAAECBAgQIECAAAECBAgQ2JiAsL0xek9MgAABAgQIECBAgAABAgQIECBAgAABAusICNvrqHkMAQIECBAgQIAAAQIECBAgQIAAAQIECGxMQNjeGL0nJkCAAAECBAgQIECAAAECBAgQIECAAIF1BITtddQ8hgABAgQIECBAgAABAgQIECBAgAABAgQ2JiBsb4zeExMgQIAAAQIECBAgQIAAAQIECBAgQIDAOgLC9jpqHkOAAAECBAgQIECAAAECBAgQIECAAAECGxMQtjdG74kJECBAgAABAgQIECBAgAABAgQIECBAYB0BYXsdNY8hQIAAAQIECBAgQIAAAQIECBAgQIAAgY0JCNsbo/fEBAgQIECAAAECBAgQIECAAAECBAgQILCOgLC9jprHECBAgAABAgQIECBAgAABAgQIECBAgMDGBITtjdF7YgIECBAgQIAAAQIECBAgQIAAAQIECBBYR0DYXkfNYwgQIECAAAECBAgQIECAAAECBAgQIEBgYwLC9sboPTEBAgQIECBAgAABAgQIECBAgAABAgQIrCMgbK+j5jEECBAgQIAAAQIECBAgQIAAAQIECBAgsDEBYXtj9J6YAAECBAgQIECAAAECBAgQIECAAAECBNYRELbXUfMYAgS2QmBnZ2fMl9WPW39948aNMV/mj9XrH/58K94JQxw7gdu929W93nq/xw7LO0yAQEbg331s++Hn4ls/P2feOYMSOEBg/nN0/5+k+/8uXX79/WPmv0a//yfp/Nny83/+Gi2BpsDqS7DV3d/89XLl+/+z/5XY/i9uvXn339y5qQlsq4Cwva2bMReBYyqwu7s75suJEyeWaL369Xy9+qJ5vj5z5sx46KGHxv3337+8zfy9++67b5w+fXqR++6778bly5fH3t7euHTp0rh27dryevV78wvr69evL9H71tfz7eev54sfBO5E4NbbXd3kD+933vUDDzyw3O6pU6f+z91euXJludN5f/Nmv/nmm+Vl/vyHt7q6Uzd7J1vytgQIrCNw6+fj+XFsvtz6cW7+en4Onh/b5ufnkydPjrNnzy4f5+bHqvlxbX58my8XLlwY33777fL5efVx7NbX8/fni2/qrbMpj7nbAru7Y5zYnf8enS/j+5edsTvD9e78DyzGOHVqZzz0wInxwP3z78bOOLE7xtmzu8vvzx+XLl8fly/vh70rV66PCxf3xrff7Y29a2NcXyL3/LfoGNev31h+vbzeG2Pv+r/GwLv9vvnzCNwqMG95/9bnne+Mnfl6+YbN/u/dfD3GOH16d5w9s7Pc+33Lre+O06d3xtkz8+u4Ma5duzEuXbo+ru3tP8Pl+Xfgyvy6a//vw3yZN7//TZ5/frNn9evl78P8/eX1/t8JMdy9EiDw7wSEbbdBgMBGBFbxen7xu/oieUbpp556avzoRz8azz333HjyySeXX8/X586dG48++uh4+OGHl5f5hfOt/7X2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AZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAWsFCLat9aQ1BBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAZsFCLZtBqZ5BBBAAAEEEEAAAQQQQAABBBBAAIH/b8cOCQAAABiE9W/9EtytAGISAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBBSidlBAAAgAElEQVQgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaARW1geMAAAewSURBVGO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUwtltPNQIECBAgQIAAAQIECBAgQIAAAQIECBA4CxjbZ2B5AgQIECBAgAABAgQIECBAgAABAgQIEGgFjO3WU40AAQIECBAgQIAAAQIECBAgQIAAAQIEzgLG9hlYngABAgQIECBAgAABAgQIECBAgAABAgRaAWO79VQjQIAAAQIECBAgQIAAAQIECBAgQIAAgbOAsX0GlidAgAABAgQIECBAgAABAgQIECBAgACBVsDYbj3VCBAgQIAAAQIECBAgQIAAAQIECBAgQOAsYGyfgeUJECBAgAABAgQIECBAgAABAgQIECBAoBUYUqkaGlqg5VYAAAAASUVORK5CYII="; diff --git a/lib/nostr.js b/lib/nostr.js index d170fed3..bc75cc96 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -5,16 +5,15 @@ import { broadcast } from "$lib/sockets"; import store from "$lib/store"; import redis from "$lib/redis"; import proxy from "@fastify/http-proxy"; -import { wait } from "$lib/utils"; +import { nada, wait } from "$lib/utils"; -app.register(proxy, { - upstream: config.nostr, - prefix: "/nostr", - rewritePrefix: "", - disableRequestLogging: true -}); +export let pool; +try { + pool = RelayPool(config.relays); +} catch(e) { + console.log(e) +} -export const pool = RelayPool(config.relays); pool.on("open", relay => { if (relay.url.includes(config.relay)) coinos = relay; @@ -168,3 +167,182 @@ pool.on("event", async (relay, sub, ev) => { console.log(e); } }); + +app.get("/event/:id", async (req, res) => { + try { + let event = JSON.parse(await redis.get(`ev:${req.params.id}`)); + let { pubkey } = event; + + event.user = JSON.parse(await redis.get(`user:${pubkey}`)) || { + username: pubkey.substr(0, 6), + pubkey, + anon: true, + follows: [], + followers: [] + }; + + res.send(event); + } catch (e) { + res.code(500).send("Problem fetching event"); + } +}); + +app.get("/:pubkey/notes", async (req, res) => { + try { + let { pubkey } = req.params; + + let user = JSON.parse(await redis.get(`user:${pubkey}`)); + + if (!user) { + user = await db.User.findOne({ + where: { pubkey } + }); + } + + if (!user) + user = { + username: pubkey.substr(0, 6), + pubkey, + anon: true, + follows: [], + followers: [] + }; + + q( + `${pubkey}:notes`, + { + kinds: [1], + authors: [pubkey] + }, + { since: 0 } + ).catch(nada); + + await redis.set(`user:${pubkey}`, JSON.stringify(user)); + + let ids = await redis.sMembers(pubkey); + + let events = ids.length + ? (await redis.mGet(ids.map(k => "ev:" + k))).map(JSON.parse) + : []; + + res.send(events.map(e => ({ ...e, user }))); + } catch (e) { + console.log(e); + res.code(500).send("problem fetching user events"); + } +}); + +app.post("/event", async (req, res) => { + let { event } = req.body; + pool.send(["EVENT", event]); + res.send(event); +}); + +app.get("/:pubkey/follows", async (req, res) => { + try { + let { pubkey } = req.params; + let { tagsonly } = req.query; + + let sub = `${pubkey}:follows`; + q( + sub, + { + limit: 1, + kinds: [3], + authors: [pubkey] + }, + { timeout: 60000, eager: 60000 } + ).catch(nada); + + let tags = JSON.parse(await redis.get(`${pubkey}:follows`)) || []; + if (tagsonly) return res.send(tags); + + let follows = []; + for (let f of tags) { + let [_, pubkey] = f; + + q(`${pubkey}:profile:f1`, { + limit: 1, + kinds: [0], + authors: [pubkey] + }).catch(nada); + + let user = JSON.parse(await redis.get(`user:${pubkey}`)); + + if (!user) + user = { + username: pubkey.substr(0, 6), + pubkey, + anon: true + }; + + follows.push(user); + } + + follows = uniq(follows, e => e.pubkey); + follows.sort((a, b) => a.username.localeCompare(b.username)); + + res.send(follows); + } catch (e) { + console.log(e); + res.code(500).send(e && e.message); + } +}); + +app.get("/:pubkey/followers", async (req, res) => { + try { + let { pubkey } = req.params; + + let pubkeys = [ + ...new Set([ + ...(await got(`${config.nostr}/followers?pubkey=${pubkey}`).json()), + ...(await redis.sMembers(`${pubkey}:followers`)) + ]) + ]; + + let followers = []; + + q( + `${pubkey}:followers`, + { kinds: [3], "#p": [pubkey] }, + { timeout: 60000, eager: 60000 } + ).catch(nada); + + for (let pubkey of pubkeys) { + let user = JSON.parse(await redis.get(`user:${pubkey}`)); + if (!user || !user.updated) { + q(`${pubkey}:profile:f2`, { + limit: 1, + kinds: [0], + authors: [pubkey] + }).catch(nada); + + user = JSON.parse(await redis.get(`user:${pubkey}`)); + } + + if (!user) + user = { + username: pubkey.substr(0, 6), + pubkey, + anon: true + }; + + followers.push(user); + } + + followers = uniq(followers, e => e.pubkey); + followers.sort((a, b) => a.username.localeCompare(b.username)); + + res.send(followers); + } catch (e) { + console.log(e); + res.code(500).send(e && e.message); + } +}); + +app.get('/nostr.json', async (req, res) => { + res.send({ + names: { adam: "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8", asoltys: "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8" } + }); +}); + diff --git a/lib/passport.js b/lib/passport.js index 8b58e305..6638fe73 100644 --- a/lib/passport.js +++ b/lib/passport.js @@ -1,7 +1,5 @@ import app from "$app"; -import db from "$db"; import jwt from "passport-jwt"; -import { Op } from "@sequelize/core"; import fastifyPassport from "@fastify/passport"; import fastifySecureSession from "@fastify/secure-session"; import config from "$config"; @@ -35,20 +33,8 @@ fastifyPassport.use( }, async (payload, next) => { try { - let { username, uuid } = payload; - let where = {}; - - if (uuid) where.uuid = uuid; - else if (username) - where.username = { [Op.or]: [username, username.replace(/ /g, "")] }; - - - let user = await db.User.findOne({ - where, - include: { model: db.Account, as: "account" } - }); - - if (user.locked) throw new Error("account is locked"); + let { uuid } = payload; + let user = await redis.get(`user:${uuid}`); next(null, user); } catch (e) { console.log("payload", payload) diff --git a/lib/persist.js b/lib/persist.js deleted file mode 100644 index fd6fb146..00000000 --- a/lib/persist.js +++ /dev/null @@ -1,22 +0,0 @@ -import fs from "fs"; - -export default (path, initial = {}) => { - if (!initial || !Object.keys(initial).length) { - try { - initial = JSON.parse(fs.readFileSync("/app/" + path)); - } catch (e) { - console.log(e); - } - } - - return new Proxy(initial, { - set(obj, prop, value) { - fs.writeFileSync( - path, - JSON.stringify({ ...obj, [prop]: value }, null, 2), - "utf-8" - ); - return Reflect.set(...arguments); - } - }); -}; diff --git a/lib/rates.js b/lib/rates.js index e896c8cc..8970ca64 100644 --- a/lib/rates.js +++ b/lib/rates.js @@ -2,19 +2,18 @@ import app from "$app"; import axios from "axios"; import config from "$config"; import fs from "fs"; -import binance from "./binance"; +// import binance from "./binance"; import { broadcast } from "./sockets"; import store from "./store"; import { err, warn } from "$lib/logging"; -let b = binance(); -setInterval(() => { - b.terminate(); - b = binance(); -}, 360000); +// let b = binance(); +// setInterval(() => { +// b.terminate(); +// b = binance(); +// }, 360000); -let fetchRates; -(fetchRates = async () => { +let fetchRates = async () => { let date, fx; try { @@ -58,7 +57,8 @@ let fetchRates; store.fx = fx; setTimeout(fetchRates, 7200000); -})(); +}; +// fetchRates(); app.get("/rate", async (req, res, next) => { res.send(store.last); diff --git a/lib/read.js b/lib/read.js deleted file mode 100644 index c021cb10..00000000 --- a/lib/read.js +++ /dev/null @@ -1,25 +0,0 @@ -export default (stream, func) => { - var remaining = ""; - - stream.on("data", function(data) { - remaining += data; - var index = remaining.indexOf("\n"); - var last = 0; - while (index > -1) { - var line = remaining.substring(last, index); - last = index + 1; - func(line); - index = remaining.indexOf("\n", last); - } - - remaining = remaining.substring(last); - }); - - stream.on("error", () => {}); - - stream.on("end", function() { - if (remaining.length > 0) { - func(remaining); - } - }); -}; diff --git a/lib/register.js b/lib/register.js index 6f8f366b..fd6f27ae 100644 --- a/lib/register.js +++ b/lib/register.js @@ -1,59 +1,49 @@ import countries from "$lib/countries"; import config from "$config"; import { authenticator } from "otplib"; -import db from "$db"; -import axios from "axios"; import bcrypt from "bcrypt"; import { l } from "$lib/logging"; +import { v4 } from "uuid"; +import redis from "$lib/redis"; export default async (user, ip, requireChallenge) => { - if (!(user && user.username)) throw new Error("Username required"); - user.username = user.username.replace(/ /g, ""); + let { password, username } = user; - let exists = await db.User.count({ where: { username: user.username } }); - if (exists) throw new Error(`Username ${user.username} taken`); + if (!username) throw new Error("Username required"); - if (user.password) { - user.password = await bcrypt.hash(user.password, 1); + username = username.replace(/ /g, ""); + let uuid = v4(); + user.uuid = uuid; + + let exists = await redis.get(`user:${username}`); + if (exists) throw new Error(`Username ${username} taken`); + + if (password) { + user.password = await bcrypt.hash(password, 1); } if (!config.ipxapi || ip.startsWith("127") || ip.startsWith("192")) { user.currency = "CAD"; } else { - let { data } = await axios.get(`https://ipxapi.com/api/ip?ip=${ip}`, { + let { countryCode } = await got(`https://ipxapi.com/api/ip?ip=${ip}`, { headers: { authorization: `Bearer ${config.ipxapi}` } - }); + }).json(); - user.currency = countries[data.countryCode] || "USD"; + user.currency = countries[countryCode] || "USD"; } user.currencies = [...new Set([user.currency, "CAD", "USD"])]; user.fiat = false; user.otpsecret = authenticator.generateSecret(); - user = await db.User.create(user); - - let account = await db.Account.create({ - user_id: user.id, - asset: config.liquid.btcasset, - balance: 0, - pending: 0, - name: "Bitcoin", - ticker: "BTC", - precision: 8 - }); - - user.accounts = [account]; - - const d = ip.split("."); - const numericIp = ((+d[0] * 256 + +d[1]) * 256 + +d[2]) * 256 + +d[3]; - if (Number.isInteger(numericIp)) { - user.ip = numericIp; - const ipExists = await db.User.findOne({ where: { ip: numericIp } }); + let d = ip.split("."); + ip = ((+d[0] * 256 + +d[1]) * 256 + +d[2]) * 256 + +d[3]; + if (Number.isInteger(ip)) { + user.ip = ip; } - user.account_id = account.id; - await user.save(); - l("new user", user.username, ip); + await redis.set(`user:${user.uuid}`, user); + await redis.set(`user:${user.username}`, user); + l("new user", user.username); return user; }; diff --git a/lib/rpc.js b/lib/rpc.js deleted file mode 100644 index 9186f4cd..00000000 --- a/lib/rpc.js +++ /dev/null @@ -1,24 +0,0 @@ -import wretch from "./wretch"; - -export default ({ host, port, wallet, username, password }) => - new Proxy( - {}, - { - get: (target, prop) => (...params) => - ((method, ...params) => { - let url = `http://${host}:${port}/wallet/${wallet}`; - return wretch() - .url(url) - .auth( - `Basic ${Buffer.from(`${username}:${password}`).toString( - "base64" - )}` - ) - .post({ - method, - params - }) - .json(({ result }) => result); - })(prop.toLowerCase(), ...params) - } - ); diff --git a/lib/send.js b/lib/send.js index eefea080..f378cfc1 100644 --- a/lib/send.js +++ b/lib/send.js @@ -1,9 +1,6 @@ import config from "$config"; -import db from "$db"; import { emit } from "$lib/sockets"; import store from "./store"; -import bolt11 from "bolt11"; -import { Op } from "@sequelize/core"; import { computeConversionFee, conversionFeeReceiver @@ -14,18 +11,7 @@ import { l, warn, err } from "$lib/logging"; export default async (amount, memo, hash, user) => { l("attempting lightning payment", user.username, amount, hash); - let payreq = bolt11.decode(hash); - let routingInfo = payreq.tags.find(t => t.tagName === "routing_info"); - if ( - routingInfo && - routingInfo.data.find(d => d.fee_proportional_millionths > 10000) - ) { - warn("fee rate too high", routingInfo); - throw new Error( - "Couldn't find a suitable route to pay to your destination" - ); - } - + // decode payreq with cl if (!amount || payreq.satoshis > amount) amount = payreq.satoshis; amount = parseInt(amount); diff --git a/lib/sendInternal.js b/lib/sendInternal.js index b49a6c64..5a8b0b9d 100644 --- a/lib/sendInternal.js +++ b/lib/sendInternal.js @@ -1,12 +1,9 @@ import config from "$config"; -import db from "$db"; import { emit } from "$lib/sockets"; import store from "$lib/store"; import { callWebhook } from "$lib/webhooks"; -import axios from "axios"; import { v4 } from "uuid"; import { notify } from "$lib/notifications"; -import { sendLiquid } from "$routes/liquid/send"; import { l, err } from "$lib/logging"; import { fail } from "$lib/utils"; @@ -153,25 +150,6 @@ export default async ( await invoice.increment({ received: amount }, { transaction }); await invoice.reload({ transaction }); - - let c = store.convert[invoice.text]; - if (c) { - l( - "internal payment detected for conversion", - invoice.text, - c.address, - recipient.username - ); - - recipient.account = a2; - - sendLiquid({ - address: c.address, - amount: amount - 100, - user: recipient, - limit: amount - }).catch(console.log); - } } let p2 = await db.Payment.create(params, { transaction }); diff --git a/lib/sockets.js b/lib/sockets.js index 5980304b..ecf15dd7 100644 --- a/lib/sockets.js +++ b/lib/sockets.js @@ -1,4 +1,4 @@ -import store from "./store"; +import store from "$lib/store"; import config from "$config"; import app from "$app"; import jwt from "jsonwebtoken"; @@ -9,7 +9,6 @@ import fastifyWs from "@fastify/websocket"; import whitelist from "./whitelist"; import { l, err, warn } from "$lib/logging"; import { getUser } from "$lib/utils"; -import db from "$db"; const code = 1000; @@ -20,8 +19,8 @@ const all = {}; const subscriptions = []; const lastSeen = {}; -export const emit = (username, type, data) => { - if (data.username !== undefined) data = pick(data, ...whitelist); +export const emit = (uuid, type, data) => { + if (data.uuid !== undefined) data = pick(data, ...whitelist); if (type === "payment" && data.amount > 0) { for (let i = subscriptions.length - 1; i >= 0; i--) { @@ -33,18 +32,18 @@ export const emit = (username, type, data) => { } } - if (!store.sockets[username]) return; - for (let id in store.sockets[username]) { - const ws = store.sockets[username][id]; + if (!store.sockets[uuid]) return; + for (let id in store.sockets[uuid]) { + const ws = store.sockets[uuid][id]; ws.send(JSON.stringify({ type, data })); } }; export const broadcast = (type, data) => { const sent = []; - for (const username in store.sockets) { - for (const id in store.sockets[username]) { - const ws = store.sockets[username][id]; + for (const uuid in store.sockets) { + for (const id in store.sockets[uuid]) { + const ws = store.sockets[uuid][id]; ws.send(JSON.stringify({ type, data })); sent.push(ws.id); } @@ -85,7 +84,6 @@ app.register(async function(app) { break; case "login": - let user; let token = data; if (lastSeen[token] > subtractSeconds(5)) return; lastSeen[token] = new Date(); @@ -93,51 +91,26 @@ app.register(async function(app) { try { if (!token || token === "null" || !jwt.decode(token)) return ws.close(code, `bad token ${token}`); - let { username, uuid } = jwt.decode(token); - if (uuid) user = await getUser(uuid); - if (user) ({ username } = user); - if (!(username || uuid)) throw new Error("Invalid JWT token"); + let { uuid } = jwt.decode(token); + if (!uuid) throw new Error("Invalid JWT token"); + let user = await getUser(uuid); + if (!user) throw new Error("User not found"); - if (!store.sockets[username]) store.sockets[username] = {}; - const existing = Object.keys(store.sockets[username]); + if (!store.sockets[uuid]) store.sockets[uuid] = {}; + const existing = Object.keys(store.sockets[uuid]); if (existing.length > 4) { const p = existing.find(sid => sid !== id); - store.sockets[username][p].send( - JSON.stringify({ type: "logout" }) - ); + store.sockets[uuid][p].send(JSON.stringify({ type: "logout" })); setTimeout(() => { - if (store.sockets[username][p]) - store.sockets[username][p].close(code, "too many sockets"); + if (store.sockets[uuid][p]) + store.sockets[uuid][p].close(code, "too many sockets"); }, 5000); } - store.sockets[username][id] = ws; - names[id] = username; - - if (!user) user = await getUser(username); + store.sockets[uuid][id] = ws; + names[id] = uuid; let { haspin } = user; - if (user.locked) throw new Error("Account is locked"); - let payments = await user.getPayments({ - where: { - account_id: user.account_id - }, - order: [["id", "DESC"]], - limit: 12, - include: [ - { - model: db.Account, - as: "account" - }, - { - model: db.Payment, - as: "fee_payment" - } - ] - }); - - let accounts = await user.getAccounts(); - let keys = await user.getKeys(); user = user.get({ plain: true }); user.accounts = accounts; @@ -154,10 +127,6 @@ app.register(async function(app) { ); } catch (e) { err("caught socket error", e.message); - if (e.message.includes("locked")) { - ws.send(JSON.stringify({ type: "locked" })); - ws.send(JSON.stringify({ type: "logout" })); - } if (e.message.includes("not found")) ws.send(JSON.stringify({ type: "logout" })); setTimeout( @@ -173,23 +142,6 @@ app.register(async function(app) { case "lnurl": store.sessions[data] = ws; break; - case "updateMemo": - const payment = await db.Payment.findOne({ - where: { - user_id: ws.user.id, - id: data.id - }, - include: { - model: db.Account, - as: "account" - } - }); - if (payment) { - payment.memo = data.memo; - await payment.save(); - emit(ws.user.username, "payment", payment); - } - break; case "subscribe": subscriptions.push({ invoice: data, ws }); break; @@ -199,11 +151,10 @@ app.register(async function(app) { }); ws.on("close", function(code, reason) { - // console.log("closing socket", reason && reason.toString(), id); try { - const username = names[id]; - if (store.sockets[username] && store.sockets[username][id]) { - delete store.sockets[username][id]; + const uuid = names[id]; + if (store.sockets[uuid] && store.sockets[uuid][id]) { + delete store.sockets[uuid][id]; } if (all[id]) delete all[id]; @@ -217,11 +168,11 @@ app.register(async function(app) { const names = {}; setInterval(() => { - for (const username in store.sockets) { - for (const id in store.sockets[username]) { - let ws = store.sockets[username][id]; + for (const uuid in store.sockets) { + for (const id in store.sockets[uuid]) { + let ws = store.sockets[uuid][id]; ws.beats++; - if (ws.beats > 10) ws.close(code, `${username} ${id} lost heartbeat`); + if (ws.beats > 10) ws.close(code, `${uuid} ${id} lost heartbeat`); } } }, 2000); diff --git a/lib/store.js b/lib/store.js index e42afeda..6f0c1c97 100644 --- a/lib/store.js +++ b/lib/store.js @@ -1,5 +1,3 @@ -import persist from "./persist"; - export default { addresses: {}, challenge: {}, @@ -13,8 +11,7 @@ export default { sockets: {}, unaccounted: [], networks: [], - convert: persist("data/conversions.json"), - rates: {}, + rates: { USD: 20000 }, fx: {}, assets: {}, bcAddressIndex: 0, diff --git a/lib/sync.js b/lib/sync.js deleted file mode 100644 index 9e2fc31c..00000000 --- a/lib/sync.js +++ /dev/null @@ -1,414 +0,0 @@ -import config from "$config"; -import { toSats } from "$lib/utils"; -import { emit } from "$lib/sockets"; -import db from "$db/db"; -import app from "$app"; -import { differenceInDays } from "date-fns"; -import fs from "fs"; -import read from "./read"; -import { Op } from "@sequelize/core"; -import store from "./store"; -import { l, err, warn } from "./logging"; -import ln from "./ln"; -import lnd from "./lnd"; -import bc from "./bitcoin"; -import lq from "./liquid"; -import { adminAuth } from "$lib/passport"; - -const init = async () => { - try { - read(fs.createReadStream("exceptions"), data => - store.exceptions.push(data) - ); - } catch (e) { - warn("couldn't read exceptions file", e.message); - } - - try { - const thirtyDays = new Date(new Date().setDate(new Date().getDate() - 30)); - - ( - await db.Invoice.findAll({ - where: { - createdAt: { - [Op.gt]: thirtyDays - } - }, - include: { - model: db.User, - as: "user" - } - }) - ).map(({ address, user, unconfidential }) => { - if (address && user) store.addresses[address] = user.username; - if (unconfidential && user) - store.addresses[unconfidential] = user.username; - }); - } catch (e) { - console.log(e); - } - - store.payments = ( - await db.Payment.findAll({ - attributes: ["hash"] - }) - ).map(p => p.hash); -}; - -init(); - -const sync = async () => { - const twoWeeksAgo = new Date(new Date().setDate(new Date().getDate() - 14)); - - try { - const payments = await db.Payment.findAll({ - attributes: ["hash", "confirmed", "preimage"], - where: { createdAt: { [Op.gt]: twoWeeksAgo } }, - include: [ - { - model: db.Account, - as: "account" - }, - { - model: db.User, - as: "user" - } - ] - }); - - let missed, invoices, recent; - if (config.lnd) { - ({ invoices } = await getInvoices({ lnd, limit: 1000 })); - - recent = invoices - .filter( - i => - i.is_confirmed && - differenceInDays(new Date(), new Date(i.created_at)) < 2 - ) - .map(i => ({ - amount: parseInt(i.received), - preimage: i.secret, - pr: i.request, - createdAt: new Date(i.created_at) - })); - } else { - ({ invoices } = await ln.listinvoices()); - - recent = invoices - .filter( - i => - i.status === "paid" && - Math.round(Date.now() / 1000) - i.paid_at < 60 * 60 * 48 - ) - .map(i => ({ - amount: parseInt(Math.round(i.msatoshi_received / 1000)), - preimage: i.payment_preimage, - pr: i.bolt11, - createdAt: new Date(i.paid_at * 1000) - })); - } - - await db.transaction(async transaction => { - const twoDaysAgo = new Date(new Date().setDate(new Date().getDate() - 2)); - let settled = ( - await db.Payment.findAll( - { - where: { - network: "lightning", - createdAt: { [Op.gt]: twoDaysAgo } - }, - attributes: ["preimage"] - }, - transaction - ) - ).map(p => p.preimage); - - missed = recent.filter(i => !settled.includes(i.preimage)); - for (let i = 0; i < missed.length; i++) { - let { amount, preimage, pr: text, createdAt } = missed[i]; - if (!text || !text.length) continue; - - try { - let invoice = await db.Invoice.findOne({ - where: { text }, - include: { - model: db.User, - as: "user" - }, - transaction - }); - - if (invoice && invoice.user_id) { - let account = await db.Account.findOne({ - where: { - user_id: invoice.user_id, - asset: config.liquid.btcasset, - pubkey: null - }, - transaction - }); - - if (account) { - let payment = await db.Payment.create({ - account_id: account.id, - user_id: invoice.user_id, - hash: text, - amount, - currency: invoice.currency, - preimage, - rate: invoice.rate, - received: true, - confirmed: true, - network: "lightning", - tip: invoice.tip, - invoice_id: invoice.id, - createdAt, - updatedAt: createdAt - }); - - await invoice.increment({ received: amount }, { transaction }); - await account.increment({ balance: amount }, { transaction }); - - l("lightning missing from account", account.id, amount); - } - } - } catch (e) { - console.log(e); - err("problem syncing lightning payment", JSON.stringify(missed[i])); - } - } - }); - - const unconfirmed = await db.Payment.findAll({ - where: { - confirmed: 0 - }, - include: [ - { - model: db.Account, - as: "account" - }, - { - model: db.Invoice, - as: "invoice" - }, - { - model: db.User, - as: "user" - } - ] - }); - - let hashes = unconfirmed.map(p => p.hash); - - const transactions = [ - ...(await bc.listTransactions("*", 500)), - ...(await lq.listTransactions("*", 500)) - ].filter( - tx => - ["send", "receive"].includes(tx.category) && - new Date(tx.time * 1000) > twoWeeksAgo - ); - - missed = transactions.filter( - tx => - tx.category === "receive" && - tx.confirmations > 2 && - hashes.includes(tx.txid) - ); - - for (let i = 0; i < missed.length; i++) { - let p = unconfirmed.find(p => p.hash === missed[i].txid); - p.confirmed = 1; - - try { - await db.transaction(async transaction => { - l("confirming payment", p.id, p.user_id, p.hash, p.address); - await p.save({ transaction }); - - if (p.invoice) - await p.invoice.increment({ received: p.amount }, { transaction }); - - await p.account.increment({ balance: p.amount }, { transaction }); - await p.account.decrement({ pending: p.amount }, { transaction }); - }); - - emit(p.user.username, "account", p.account); - emit(p.user.username, "payment", p); - } catch (e) { - err("problem confirming payment", p.id, e.message, e.stack); - } - } - - store.unaccounted = []; - - transactions.map(tx => { - if ( - !payments.find(p => p.hash === tx.txid) && - !store.exceptions.includes(tx.txid) && - !store.unaccounted.find(a => a.txid === tx.txid) && - new Date(tx.blocktime * 1000) >= twoWeeksAgo - ) { - store.unaccounted.push(tx); - } - }); - - if (store.unaccounted.length) - warn( - "wallet transactions missing from database", - store.unaccounted.map(tx => tx.txid) - ); - - let receipts = store.unaccounted - .filter( - tx => - tx.category === "receive" && - (!tx.asset || tx.asset === config.liquid.btcasset) - ) - .map(tx => tx.address); - - invoices = await db.Invoice.findAll({ - where: { - [Op.or]: { - address: receipts, - unconfidential: receipts - } - }, - include: [ - { - model: db.Account, - as: "account" - }, - { - model: db.User, - as: "user" - } - ] - }); - - for (let i = 0; i < invoices.length; i++) { - let { - address, - unconfidential, - account, - account_id, - currency, - id: invoice_id, - network, - rate, - tip, - user, - user_id - } = invoices[i]; - - l("unaccounted", address); - - let pending = store.unaccounted.filter( - tx => tx.address === address || tx.address === unconfidential - ); - - for (let i = 0; i < pending.length; i++) { - let { amount, txid: hash } = pending[i]; - - amount = toSats(amount); - - try { - await db.transaction(async transaction => { - l("found pending transaction", user.username, amount, hash); - store.payments.push(hash); - let p = await db.Payment.create( - { - account_id, - address: address || unconfidential, - amount, - confirmed: false, - currency, - hash, - invoice_id, - network, - rate, - received: true, - tip, - user_id - }, - { transaction } - ); - - await account.increment({ pending: amount }, { transaction }); - - emit(user.username, "account", account); - emit(user.username, "payment", p); - }); - } catch (e) { - err("problem updating pending transaction", hash, e.message, e.stack); - } - } - } - } catch (e) { - console.log(e); - err("sync check failed", e.message, e.stack); - } - - setTimeout(sync, 300000); -}; - -setTimeout(sync, 5000); - -app.get("/conversions", async (req, res) => { - const transactions = [ - ...(await bc.listTransactions("*", 500)), - ...(await lq.listTransactions("*", 500)) - ].filter(tx => ["receive"].includes(tx.category)); - - const { addr } = req.query; - const { invoices } = require("./invoices"); - const persist = require("./persist"); - let conversions = persist("data/conversions.json"); - const payments = ( - await db.Payment.findAll({ - attributes: ["hash"] - }) - ).map(p => p.hash); - res.send( - Object.keys(conversions) - .filter(c => conversions[c].address === addr) - .filter(c => { - return ( - invoices.find( - i => i.payment_request === c && i.state === "SETTLED" - ) || transactions.find(tx => tx.txid === c) - ); - }) - .filter(c => !payments.includes(c)) - ); -}); - -app.post("/renameImages", adminAuth, async (req, res) => { - let d = './uploads/'; - fs.readdir(d, (err, files) => { - files.forEach(async file => { - try { - let m = file.match(/(.*)-profile/); - - let username; - - if (m && m[1]) username = m[1]; - else m = file.match(/(.*)-banner/); - if (m && m[1]) username = m[1]; - - console.log(username); - - if (username) { - let { uuid } = await db.User.findOne({ where: { username } }); - fs.rename(d + file, d + uuid + file.replace(username, ""), function(err) { - if (err) console.log("ERROR: " + err); - }); - } - } catch (e) { - console.log(e); - } - }); - }); - res.send({}); -}); diff --git a/lib/utils.js b/lib/utils.js index 95ee5fb1..1513138b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,16 +1,8 @@ import store from "$lib/store"; import redis from "$lib/redis"; import config from "$config"; -import db from "$db"; -import sequelize from "@sequelize/core"; -import { fromBase58 } from "bip32"; -import { payments as bcPayments, networks as bcNetworks } from "bitcoinjs-lib"; -import { payments as lqPayments, networks as lqNetworks } from "liquidjs-lib"; -import lnd from "$lib/lnd"; import ln from "$lib/ln"; -const { Op } = sequelize; - export const nada = () => {}; export const sleep = n => new Promise(r => setTimeout(r, n)); @@ -26,57 +18,13 @@ export const requirePin = async ({ body, user }) => { throw new Error("Invalid pin"); }; -export const getUser = async (username, transaction) => { - let params = { - include: [ - { - model: db.Account, - as: "account" - } - ], - where: { - [Op.or]: { - username: { [Op.or]: [username, username.replace(/ /g, "")] }, - uuid: { [Op.or]: [username, username.replace(/ /g, "")] } - } - } - }; - - if (transaction) { - params.lock = transaction.LOCK.UPDATE; - params.transaction = transaction; - } - - let user = await db.User.findOne(params); - if (!user) throw new Error(`User ${username} not found`); - +export const getUser = async (uuid) => { + let user = await redis.get(`user:${uuid}`); user.haspin = !!user.pin; return user; }; -export const getUserById = async (id, transaction) => { - let params = { - include: [ - { - model: db.Account, - as: "account" - } - ], - where: { id } - }; - - if (transaction) { - params.lock = transaction.LOCK.UPDATE; - params.transaction = transaction; - } - - let user = await db.User.findOne(params); - if (!user) throw new Error("User not found"); - - return user; -}; - export const prod = process.env.NODE_ENV === "production"; export const fail = msg => { throw new Error(msg); @@ -85,98 +33,6 @@ export const fail = msg => { export const SATS = 100000000; export const toSats = n => Math.round(n * SATS); -export const getBlockHeight = message => { - let buffer = Buffer.from(message, "hex"); - if (buffer.length < 80) throw new Error("Buffer too small (< 80 bytes)"); - - let offset = 0; - const readSlice = n => { - offset += n; - return buffer.slice(offset - n, offset); - }; - - const readUInt32 = () => { - const i = buffer.readUInt32LE(offset); - offset += 4; - return i; - }; - - const readUInt8 = () => { - const i = buffer.readUInt8(offset); - offset += 1; - return i; - }; - - const readVarInt = () => { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - }; - - readUInt32(); - readSlice(32); - readSlice(32); - readUInt32(); - return readUInt32(); -}; - -export const deriveAddress = async (account, type) => { - let { index, pubkey, path, network } = account; - let n = { bitcoin: bcNetworks, liquid: lqNetworks }[network][ - prod ? "bitcoin" : "regtest" - ]; - let p = { bitcoin: bcPayments, liquid: lqPayments }[network]; - let root = fromBase58(pubkey, n); - let parts = path.split("/"); - let hd = root.derive(parseInt(parts[parts.length - 1])); - - type = { - bech32: "p2wpkh", - "p2sh-segwit": "p2sh", - legacy: "p2pkh" - }[type]; - - console.log("TYPE", type); - - let r; - if (type !== "p2sh") { - r = p[type]({ - pubkey: hd.publicKey, - network: n - }); - } else { - r = p[type]({ - redeem: p.p2wpkh({ - pubkey: hd.publicKey, - network: n - }), - network: n - }); - } - - index++; - account.index = index; - account.address = r.address; - await account.save(); - - return r; -}; - -export const bip21 = ({ address, amount, memo, tip, network }, { asset }) => { - if (network === "liquid") network = "liquidnetwork"; - - let url = amount || memo ? `${network}:${address}?` : address; - if (amount) - url += `amount=${((amount + tip) / SATS).toFixed(8)}${memo ? "&" : ""}`; - if (memo) url += `message=${memo}`; - - if (network === "liquidnetwork" && amount) { - url += `&asset=${asset}`; - } - - return url; -}; - export const derivePayRequest = async ({ amount, memo, tip }) => { if (!tip) tip = 0; let value = amount + tip; diff --git a/lib/wretch.js b/lib/wretch.js deleted file mode 100644 index 0be00d10..00000000 --- a/lib/wretch.js +++ /dev/null @@ -1,4 +0,0 @@ -import wretch from "wretch"; -import fetch from "node-fetch"; -wretch().polyfills({ fetch }); -export default wretch; diff --git a/mine.sh b/mine.sh deleted file mode 100755 index 0df5b6ba..00000000 --- a/mine.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - - -while true -do - docker exec -it liquid elements-cli -datadir=/config generatetoaddress 1 $(docker exec -it liquid elements-cli -datadir=/config getnewaddress) - sleep 8 -done diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index 44ce3e3b..00000000 --- a/nodemon.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ignore": ["data/*"] -} diff --git a/package.json b/package.json index ad806617..631f61e9 100644 --- a/package.json +++ b/package.json @@ -7,49 +7,19 @@ "dev": "nodemon index.js" }, "dependencies": { - "@fastify/http-proxy": "^8.4.0", "@fastify/multipart": "^7.1.2", "@fastify/passport": "^2.2.0", "@fastify/secure-session": "^5.2.0", "@fastify/static": "^6.5.0", "@fastify/websocket": "^7.0.1", - "@sequelize/core": "7.0.0-alpha.10", - "async-mutex": "^0.2.4", - "axios": "^0.21.1", "bcrypt": "^5.0.0", - "bech32": "^2.0.0", - "bip32": "^2.0.5", - "bip39": "^3.0.2", - "bitcoinjs-lib": "6", - "body-parser": "^1.19.0", - "bolt11": "^1.3.2", - "buffer-reverse": "^1.0.1", "clightning-client": "^0.1.2", - "coinselect": "^3.1.12", - "compression": "^1.7.4", - "cookie-parser": "^1.4.4", - "cors": "^2.8.5", "date-fns": "^2.21.3", - "ecpair": "^2.0.1", - "express": "4.17.1", - "express-async-handler": "^1.1.4", - "express-pino-logger": "^6.0.0", - "express-session": "^1.16.2", "fastify": "^4.5.3", "file-type": "^18.0.0", "got": "^12.5.3", - "http-proxy-middleware": "^0.20.0", - "imap": "^0.8.19", "jsonwebtoken": "^8.5.1", - "knex": "^2.3.0", - "liquidjs-lib": "6.0.2-liquid.8", "lnurl": "^0.24.2", - "long": "^4.0.0", - "mailgun-js": "^0.22.0", - "mariadb": "^2.4.0", - "mysql": "^2.18.1", - "mysql2": "^2.2.5", - "node-fetch": "^2.6.1", "nodemon": "^2.0.7", "nostr": "^0.2.7", "otplib": "^12.0.1", @@ -60,16 +30,11 @@ "postmark": "^3.0.11", "pump": "^3.0.0", "query-string": "^6.13.1", - "readable-stream-clone": "^0.0.7", "redis": "^4.3.1", "sharp": "^0.31.1", - "sqlite3": "^5.0.11", - "tiny-secp256k1": "^2.1.2", "uuid": "8", "web-push": "^3.4.4", - "wretch": "^1.7.4", - "ws": "^7.4.6", - "zeromq": "6.0.0-beta.6" + "ws": "^7.4.6" }, "devDependencies": { "prettier": "^1.19.1" diff --git a/prod.yml b/prod.yml deleted file mode 100644 index 58729359..00000000 --- a/prod.yml +++ /dev/null @@ -1,96 +0,0 @@ -version: '3.6' -services: - app: - depends_on: - - cl - - maria - - bitcoin - - liquid - container_name: app - image: gcr.io/coinos-326717/github.com/coinos/coinos-server:prod - restart: always - environment: - NODE_ENV: production - NODE_OPTIONS: "--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js --openssl-legacy-provider" - HOSTNAME: "${HOSTNAME}" - volumes: - - ./:/app - - ./logs:/logs - networks: - default: - ipv4_address: 172.42.0.22 - maria: - container_name: maria - image: mariadb - restart: always - environment: - MARIADB_ROOT_PASSWORD: password - volumes: - - ./mysql:/var/lib/mysql - bitcoin: - image: asoltys/bitcoin - container_name: bitcoin - volumes: - - ./config/bitcoin:/home/bitcoin/.bitcoin - restart: always - networks: - default: - ipv4_address: 172.42.0.23 - liquid: - image: ghcr.io/vulpemventures/elements - container_name: liquid - volumes: - - ./config/liquid:/home/elements/.elements - restart: always - networks: - default: - ipv4_address: 172.42.0.24 - cl: - depends_on: - - bitcoin - image: elementsproject/lightningd:v22.11.1 - container_name: cl - restart: always - volumes: - - ./config/lightning:/root/.lightning - ports: - - "9736:9735" - networks: - default: - ipv4_address: 172.42.0.25 - lnd: - depends_on: - - bitcoin - - tor - image: lightninglabs/lnd:v0.15.4-beta - container_name: lnd - volumes: - - ./config/lnd:/root/.lnd - - ./config/bitcoin:/root/.bitcoin - - ./config/tor:/etc/tor - ports: - - "9735:9735" - restart: always - networks: - default: - ipv4_address: 172.42.0.26 - tor: - container_name: tor - image: dperson/torproxy - restart: always - volumes: - - ./config/tor:/etc/tor - - ./config/tordata:/var/lib/tor - networks: - default: - ipv4_address: 172.42.0.27 - redis: - restart: always - container_name: redis - image: redis - volumes: - - ./config/redis:/data -networks: - default: - name: net - external: true diff --git a/reg.yml b/reg.yml deleted file mode 100644 index e03d9352..00000000 --- a/reg.yml +++ /dev/null @@ -1,96 +0,0 @@ -version: '3.6' -services: - app: - depends_on: - - cl - - maria - - bitcoin - - liquid - container_name: app - image: gcr.io/coinos-326717/github.com/coinos/coinos-server:prod - restart: always - environment: - NODE_ENV: production - NODE_OPTIONS: "--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js --openssl-legacy-provider" - HOSTNAME: "${HOSTNAME}" - volumes: - - ./:/app - - ./logs:/logs - networks: - default: - ipv4_address: 172.42.0.22 - maria: - container_name: maria - image: mariadb - restart: always - environment: - MARIADB_ROOT_PASSWORD: password - volumes: - - ./mysql:/var/lib/mysql - bitcoin: - image: ghcr.io/vulpemventures/bitcoin - container_name: bitcoin - volumes: - - ./config/bitcoin:/home/bitcoin/.bitcoin - restart: always - networks: - default: - ipv4_address: 172.42.0.23 - liquid: - image: ghcr.io/vulpemventures/elements - container_name: liquid - volumes: - - ./config/liquid:/home/elements/.elements - restart: always - networks: - default: - ipv4_address: 172.42.0.24 - cl: - depends_on: - - bitcoin - image: elementsproject/lightningd:v0.12.0 - container_name: cl - restart: always - volumes: - - ./config/lightning:/root/.lightning - ports: - - "9736:9735" - networks: - default: - ipv4_address: 172.42.0.25 - lnd: - depends_on: - - bitcoin - - tor - image: lightninglabs/lnd:v0.15.4-beta - container_name: lnd - volumes: - - ./config/lnd:/root/.lnd - - ./config/bitcoin:/root/.bitcoin - - ./config/tor:/etc/tor - ports: - - "9735:9735" - restart: always - networks: - default: - ipv4_address: 172.42.0.26 - tor: - container_name: tor - image: dperson/torproxy - restart: always - volumes: - - ./config/tor:/etc/tor - - ./config/tordata:/var/lib/tor - networks: - default: - ipv4_address: 172.42.0.27 - redis: - restart: always - container_name: redis - image: redis - volumes: - - ./config/redis:/data -networks: - default: - name: net - external: true diff --git a/routes/admin.js b/routes/admin.js deleted file mode 100644 index 3053d813..00000000 --- a/routes/admin.js +++ /dev/null @@ -1,566 +0,0 @@ -import express from "express"; -var router = express.Router(); - -import * as parseInput from "../scripts/apiParse"; -import Sequelize from "@sequelize/core"; -const Op = Sequelize.Op; - -/** - * @api {get} /users Retrieve user list - * @apiName getUsers - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess {Object} users List of users - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "users" : [ - * { "username": 'John Doe', "email": 'johndoe@gmail.com', ...}, - * { "username": 'Jane Doe', "email": 'janedoe@gmail.com', ...} - * ] - * } - */ -router.get( - "/users", - // auth, - async (req, res) => { - var { search, starts_with, contains } = req.query; - var users = knex - .select( - "username", - "email", - "phone", - "verified", - knex.raw("LEFT(createdAt,10) as created_at") - ) - .from("users"); - - var timeCondition = parseInput.addTimeSearch(req.query, "users.createdAt"); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) users = users.whereRaw(timeCondition); - if (userCondition) users = users.whereRaw(userCondition); - - // Alternative Sequelize query syntax - // await db.User.findAll({ - // attributes: ['username', 'email', 'phone', 'verified', 'createdAt'] - // }) - - const found = await users; - return res.send({ users: found }); - } -); - -/** - * @api {get} /referrals Retrieve list of referral tokens - * @apiName referrals - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of referral tokens - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "referrals" : [ - * { "token": '***', "status": 'pending', sponsor: 'Adam', user: 'newUsername1', expiry: null }, - * { "token": '***', "status": 'pending', sponsor: 'Adam', user: 'newUsername2', expiry: null }, - * ] - * } - */ -router.get("/referrals", auth, async (req, res) => { - var referrals = knex - .select( - "token", - "users.username as user", - "sponsor.username as sponsor", - knex.raw("LEFT(created_at, 10) as created_at"), - "referrals.status" - ) - .from("referrals") - .leftJoin("users", "users.id", "referrals.user_id") - .leftJoin("users as sponsor", "sponsor.id", "referrals.sponsor_id"); - - // Alternative Sequelize query syntax (returns sub-tables as hash) - // var referrals = await db.Referral.findAll({ - // include: [ - // {model: db.User, as: 'user', attributes: [['username', 'user']]}, - // {model: db.User, as: 'sponsor', attributes: [['username', 'sponsor']]} - // ] - // }) - - var timeCondition = parseInput.addTimeSearch( - req.query, - "referrals.updated_at" - ); - var userCondition = parseInput.addUserSearch(req.query, "sponsor"); - - if (userCondition) { - referrals = referrals.whereRaw(userCondition); - } - if (timeCondition) { - referrals = referrals.whereRaw(timeCondition); - } - - var found = await referrals; - return res.send({ referrals: found }); -}); - -/** - * @api {get} /waiting_list Retrieve waiting_list - * @apiName waiting_list - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of referral tokens - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "waiting_list" : [ - * { email: "johndoe@hotmail.ca", phone: '604 123-4567 }, - * { email: "janedoe@hotmail.ca", phone: '604 246-8910 }, - * ] - * } - */ -router.get( - "/waiting_list", - // auth, - async (req, res) => { - var { search, starts_with, contains } = req.query; - - var queue = knex - .select( - "waiting_list.email", - "waiting_list.phone", - "waiting_list.created_at as requested", - "users.id as current_user_id" - ) - .from("waiting_list") - .leftJoin("users", "users.email", "waiting_list.email") - .where("waiting_list.id", ">", 0); - - var timeCondition = parseInput.addTimeSearch( - req.query, - "waiting_list.updated_at" - ); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) queue = queue.whereRaw(timeCondition); - if (userCondition) queue = queue.whereRaw(userCondition); - - const found = await queue; - - return res.send({ queue: found }); - } -); - -/** - * @api {get} /accounts Retrieve list of accounts - * @apiName accounts - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of accounts - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "accounts" : [ - * { "user": 'Adam', "account_id": '123', balance: 500 ... }, - * ... - * ] - * } - */ -router.get("/accounts", auth, async (req, res) => { - var { nonZero, search, starts_with, contains } = req.query; - - var accounts = knex - .select( - "username", - "accounts.id as account_id", - "ticker", - "balance", - knex.raw("LEFT(accounts.createdAt, 10) as created"), - knex.raw("LEFT(accounts.updatedAt, 10) as updated") - ) - .from("accounts") - .leftJoin("users", "users.id", "accounts.user_id"); - - if (nonZero) { - accounts = accounts.where("balance", ">", 0); - } - - var timeCondition = parseInput.addTimeSearch(req.query, "accounts.updatedAt"); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) accounts = accounts.whereRaw(timeCondition); - if (userCondition) accounts = accounts.whereRaw(userCondition); - - const found = await accounts; - - return res.send({ accounts: found }); -}); - -/** - * @api {get} /transactions Retrieve summary of user transactions - * - * @apiName user_transactions - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of invoices, payments, orders for users - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "transactions" : [ - * { "username": 'Adam', "email": 'adam@gmail.com', invoices: 1, orders: 0, payments: 2 }, - ... - * ] - * } - */ -router.get( - "/transactions", - // auth, - async (req, res) => { - var { type, since, search, starts_with, contains } = req.query; - - var transactions = knex - .select( - "users.username", - "users.email" - // knex.raw('LEFT(created_at, 10) as created') - // knex.raw('LEFT(updatedAt, 10) as updated') - // knex.raw('COUNT(invoices.id) as invoices'), - // knex.raw('COUNT(payments.id) as payments'), - // knex.raw('COUNT(orders.id) as orders') - ) - .from("users"); - - const types = ["deposits", "withdrawals", "orders", "payments", "invoices"]; - for (var i = 0; i < types.length; i++) { - var stamp = types[i].replace(/s$/, "") + "_date"; - - transactions = transactions - .select( - knex.raw("COUNT(DISTINCT " + types[i] + ".id) as " + types[i]), - knex.raw("LEFT(MAX(" + types[i] + ".updatedAt), 10) as " + stamp) - ) - .leftJoin(types[i], "users.id", types[i] + ".user_id"); - // .havingRaw(types[i] + ' > ?', [0]) - - if (since) { - transactions = transactions.havingRaw( - "(" + stamp + " >= ? OR " + stamp + " IS NULL)", - [since] - ); - } - } - - if (since) - transactions = transactions.havingRaw( - "invoices + payments + deposits + orders > ?", - [0] - ); - - var userCondition = parseInput.addUserSearch(req.query); - if (userCondition) transactions = transactions.whereRaw(userCondition); - - transactions = transactions.groupBy("users.id"); - - const found = await transactions; - - return res.send({ transactions: found }); - } -); - -/** - * @api {get} /orders Retrieve summary of orders - * - * @apiName orders - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of orders - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "orders" : [ - * { "seller": 'Adam', "buyer": 'bob' ... }, - ... - * ] - * } - */ -router.get("/orders", auth, async (req, res) => { - var { type, since, search, starts_with, contains } = req.query; - - var orders = knex - .select( - "u1.username as seller", - "u2.username as buyer", - "v1 as sell_amount", - "v2 as buy_amount", - "rate", - "a1.name as from", - "a2.name as to", - knex.raw("Left(completedAt,16) as completed") - ) - .from("orders") - .leftJoin("accounts as a1", "a1.id", "orders.a1_id") - .leftJoin("accounts as a2", "a2.id", "orders.a2_id") - .leftJoin("users as u1", "a1.user_id", "u1.id") - .leftJoin("users as u2", "a2.user_id", "u2.id"); - - var timeCondition = parseInput.addTimeSearch(req.query, "orders.updatedAt"); - if (timeCondition) orders = orders.whereRaw(timeCondition); - - var userCondition1 = parseInput.addUserSearch(req.query, "u1"); - var userCondition2 = parseInput.addUserSearch(req.query, "u2"); - - if (userCondition1) - orders = orders.where(function() { - this.whereRaw(userCondition1); - this.orWhere(knex.raw(userCondition2)); - }); - - var found = await orders; - return res.send({ orders: found }); -}); - -/** - * @api {get} /invoices Retrieve summary of invoices - * - * @apiName invoices - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of invoices - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "invoices" : [ - * { "seller": 'Adam', "buyer": 'bob' ... }, - ... - * ] - * } - */ -router.get("/invoices", auth, async (req, res) => { - var { type, since, search, starts_with, contains } = req.query; - - var invoices = knex - .select( - "username", - "users.email", - "invoices.currency", - "invoices.amount", - "accounts.network", - knex.raw("Left(invoices.updatedAt,16) as updated") - ) - .from("invoices") - .leftJoin("accounts", "invoices.account_id", "accounts.id") - .leftJoin("users", "accounts.user_id", "users.id"); - - var timeCondition = parseInput.addTimeSearch(req.query, "invoices.updatedAt"); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) invoices = invoices.whereRaw(timeCondition); - if (userCondition) invoices = invoices.whereRaw(userCondition); - - var found = await invoices; - return res.send({ invoices: found }); -}); - -/** - * @api {get} /deposits Retrieve summary of deposits - * - * @apiName deposits - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of deposits - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "deposits" : [ - * { "seller": 'Adam', "buyer": 'bob' ... }, - ... - * ] - * } - */ -router.get("/deposits", auth, async (req, res) => { - var { type, since, search, starts_with, contains } = req.query; - - var deposits = knex - .select( - "username", - "users.email", - "amount", - "credited", - knex.raw("Left(deposits.updatedAt,16) as deposited") - ) - .from("deposits") - .leftJoin("users", "deposits.user_id", "users.id"); - - var timeCondition = parseInput.addTimeSearch(req.query, "deposits.updatedAt"); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) deposits = deposits.whereRaw(timeCondition); - if (userCondition) deposits = deposits.whereRaw(userCondition); - - var found = await deposits; - return res.send({ deposits: found }); -}); - -/** - * @api {get} /withdrawals Retrieve summary of withdrawals - * - * @apiName withdrawals - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of withdrawals - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "withdrawals" : [ - * { "seller": 'Adam', "buyer": 'bob' ... }, - ... - * ] - * } - */ -router.get("/withdrawals", auth, async (req, res) => { - var { type, since, search, starts_with, contains } = req.query; - - var withdrawals = knex - .select( - "username", - "users.email", - "amount", - "notes", - knex.raw("Left(withdrawals.updatedAt,16) as withdrawn") - ) - .from("withdrawals") - .leftJoin("users", "withdrawals.user_id", "users.id"); - - var timeCondition = parseInput.addTimeSearch( - req.query, - "withdrawals.updatedAt" - ); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) withdrawals = withdrawals.whereRaw(timeCondition); - if (userCondition) withdrawals = withdrawals.whereRaw(userCondition); - - var found = await withdrawals; - return res.send({ withdrawals: found }); -}); - -/** - * @api {get} /payments Retrieve summary of payments - * - * @apiName payments - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of payments - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "payments" : [ - * { "seller": 'Adam', "buyer": 'bob' ... }, - ... - * ] - * } -**/ -router.get("/payments", auth, async (req, res) => { - var { type, since, search, starts_with, contains } = req.query; - - var payments = knex - .select( - "username", - "users.email", - "amount", - "payments.network", - knex.raw("LEFT(hash,20) as hash"), - knex.raw("Left(payments.updatedAt,16) as deposited") - ) - .from("payments") - .leftJoin("accounts", "payments.account_id", "accounts.id") - .leftJoin("users", "accounts.user_id", "users.id"); - - var timeCondition = parseInput.addTimeSearch(req.query, "payments.updatedAt"); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) payments = payments.whereRaw(timeCondition); - if (userCondition) payments = payments.whereRaw(userCondition); - - var found = await payments; - return res.send({ payments: found }); -}); - -/** - * @api {get} /user_kyc Retrieve user kyc details - * - * @apiName user_kyc - * @apiGroup Admin - * - * @apiPermission admin - * - * @apiSuccess returns list of kyc details for each user - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "users" : [ - * { "username": 'Adam', "email": 'adam@gmail.com', ... kyc_data... }, - ... - * ] - * } - */ -router.get("/kyc_required", auth, async (req, res) => { - var { since, search, starts_with, contains } = req.query; - - var threshold = "0.2"; - - var kyc = knex - .select( - knex.raw("CONCAT( FORMAT(Max(amount)/1000000, 1), ' M') as max"), - "username", - "users.email", - "users.verified as kyc_verified", - // 'payments.currency', - // 'payments.network', - // knex.raw('LEFT(hash,20) as hash'), - // knex.raw('Left(payments.updatedAt,16) as transferred') - // knex.raw( parseInput.readableDate('Max(payments.updatedAt)', 'last_changed') ) - knex.raw( - "DATE_FORMAT(Max(payments.updatedAt), ?) AS last_changed", - "%M %d, %Y" - ) - ) - .from("payments") - .leftJoin("accounts", "payments.account_id", "accounts.id") - .leftJoin("users", "payments.user_id", "users.id") // should really point to accounts - .groupBy("users.id"); - // .groupBy('payments.currency') - // .groupBy('payments.network') - - var timeCondition = parseInput.addTimeSearch(req.query, "payments.updatedAt"); - var userCondition = parseInput.addUserSearch(req.query); - - if (timeCondition) kyc = kyc.whereRaw(timeCondition); - if (userCondition) kyc = kyc.whereRaw(userCondition); - - var found = await kyc.having("max", ">=", threshold); - - return res.send({ found: found }); -}); - -export default router; diff --git a/routes/assets.js b/routes/assets.js deleted file mode 100644 index ed0d3036..00000000 --- a/routes/assets.js +++ /dev/null @@ -1,536 +0,0 @@ -import { emit } from "$lib/sockets"; -import store from "$lib/store"; -import db from "$db"; -import config from "$config"; -import app from "$app"; -import { auth } from "$lib/passport"; -import axios from "axios"; -import crypto from "crypto"; -import fs from "fs"; -import { err, l } from "$lib/logging"; -import lq from "$lib/liquid"; -import { SATS } from "$lib/utils"; - -let fetchAssets; -(fetchAssets = async () => { - try { - const { data } = await axios.get("https://assets.blockstream.info/"); - - store.assets = data; - } catch (e) { - var liquid_assets = JSON.parse(fs.readFileSync("/app/assets.json")); - if (liquid_assets) { - store.assets = liquid_assets; - console.debug("using static assets..." + e.message); - } else { - err("error fetching assets", e.message); - res.code(500).send("error fetching assets"); - } - } - - setTimeout(fetchAssets, 7200000); -})(); - -app.get("/assets", async (req, res) => { - if (store.assets) { - const accounts = await db.Account.findAll({ - // group: ["asset"] - }); - Object.keys(store.assets).map(a => { - store.assets[a].registered = true; - if (!store.assets[a].asset) - store.assets[a].asset = store.assets[a].asset_id; - if ( - (store.assets[a].ticker === "BTC" && - store.assets[a].asset !== config.liquid.btcasset) || - (store.assets[a].ticker === "EUR" && - store.assets[a].asset !== config.liquid.eurasset) || - (store.assets[a].ticker === "CAD" && - store.assets[a].asset !== config.liquid.cadasset) || - (store.assets[a].ticker === "USDt" && - store.assets[a].asset !== config.liquid.usdtasset) - ) - delete store.assets[a]; - }); - accounts.map(({ asset, name, domain, ticker, precision }) => { - if (!store.assets[asset]) - store.assets[asset] = { - asset, - name, - domain, - ticker, - precision - }; - }); - - res.send(store.assets); - } else { - console.log("error getting blockstream assets"); - res.code(500).send("Problem fetching blockstream asset registry data"); - } -}); - -app.post("/assets", auth, async (req, res) => { - try { - const sha256 = crypto.createHash("sha256"); - const { id: user_id } = req.user; - const blind = false; - - if (!user_id) throw new Error("unauthorized"); - - const { - address, - domain, - name, - asset_amount, - filename, - token_amount, - precision, - pubkey, - ticker - } = req.body; - - const asset_address = address - ? address - : await lq.getNewAddress("", "legacy"); - - const token_address = - token_amount && (await lq.getNewAddress("", "legacy")); - - const issuer_pubkey = pubkey - ? pubkey - : (await lq.getAddressInfo(asset_address)).pubkey; - const version = 0; - - let contract = { - entity: { domain }, - issuer_pubkey, - name, - precision, - version - }; - - if (filename) contract.file = filename; - if (ticker) contract.ticker = ticker; - - contract = Object.keys(contract) - .sort() - .reduce((r, k) => ((r[k] = contract[k]), r), {}); - - l( - "attempting issuance", - req.user.username, - JSON.stringify(contract), - "address", - address, - "pubkey", - pubkey - ); - - sha256.update(JSON.stringify(contract)); - const hash = sha256.digest("hex"); - const contract_hash = hash - .match(/[a-f0-9]{2}/g) - .reverse() - .join(""); - const rawtx = await lq.createRawTransaction([], [{ data: "00" }]); - const funded = await lq.fundRawTransaction(rawtx, { feeRate: 0.000002 }); - const params = { - asset_amount, - asset_address, - blind, - contract_hash - }; - - params.asset_amount = asset_amount / (SATS / 10 ** precision); - - if (token_amount) { - params.token_amount = token_amount; - params.token_address = token_address; - params.token_amount = parseInt(params.token_amount); - } - - let ria; - try { - ria = await lq.rawIssueAsset(funded.hex, [params]); - } catch (e) { - l(asset_amount, token_amount, params); - throw new Error(e.message); - } - - const { asset, hex, token } = ria[0]; - const brt = await lq.blindRawTransaction(hex, true, [], false); - const srt = await lq.signRawTransactionWithWallet(brt); - const allowed = (await lq.testMempoolAccept([srt.hex]))[0].allowed; - if (!allowed) throw new Error("issuance rejected by mempool", srt.hex); - const txid = await lq.sendRawTransaction(srt.hex); - - await db.transaction(async transaction => { - let account = await db.Account.findOne({ - where: { - user_id, - asset: config.liquid.btcasset, - pubkey: null - }, - include: { - model: db.User, - as: "user" - }, - order: [["balance", "DESC"]], - lock: transaction.LOCK.UPDATE, - transaction - }); - - let { user } = account; - - if (Math.round(funded.fee * SATS) > account.balance) { - err( - "amount exceeds balance", - asset_amount, - funded.fee, - account.balance - ); - throw new Error(`Insufficient funds to pay fee of ${funded.fee} BTC`); - } - - await account.decrement( - { balance: Math.round(funded.fee * SATS) }, - { transaction } - ); - await account.reload({ transaction }); - emit(user.username, "account", account); - - let invoice; - let recipient = user; - if (address) { - invoice = await db.Invoice.findOne({ - where: { address }, - include: { - model: db.User, - as: "user" - } - }); - recipient = invoice.user; - } - - if (!invoice) { - invoice = await db.Invoice.create( - { - account_id: account.id, - user_id, - amount: Math.round(params.asset_amount * SATS), - address: asset_address, - currency: user.currency, - rate: store.rates[user.currency], - network: "liquid" - }, - { transaction } - ); - } - - account = await db.Account.create( - { - asset, - contract, - domain, - user_id: invoice.user_id, - ticker, - precision, - name, - balance: 0, - network: "liquid", - pending: Math.round(params.asset_amount * SATS) - }, - { transaction } - ); - - emit(recipient.username, "account", account); - l( - "issued asset", - user.username, - params.asset_amount, - ticker, - name, - account.id - ); - - const asset_payment = await db.Payment.create( - { - account_id: account.id, - user_id: invoice.user_id, - invoice_id: invoice.id, - hash: txid, - amount: Math.round(params.asset_amount * SATS), - received: true, - confirmed: false, - address: asset_address, - network: "liquid" - }, - { transaction } - ); - emit(recipient.username, "payment", asset_payment); - - if (invoice.user_id !== user.id) { - const asset_send = await db.Payment.create( - { - account_id: account.id, - user_id, - hash: txid, - amount: -Math.round(params.asset_amount * SATS), - received: false, - confirmed: false, - address, - network: "liquid" - }, - { transaction } - ); - emit(recipient.username, "payment", asset_send); - } - - store.issuances[txid] = { - user_id, - asset, - asset_amount: params.asset_amount, - asset_payment_id: asset_payment.id - }; - - if (token_amount) { - invoice = null; - recipient = user; - - if (address) { - invoice = await db.Invoice.findOne({ - where: { address }, - include: { - model: db.User, - as: "user" - } - }); - recipient = invoice.user; - } - - if (!invoice) { - invoice = await db.Invoice.create( - { - account_id: account.id, - user_id, - amount: Math.round(params.token_amount * SATS), - address: token_address, - currency: user.currency, - rate: store.rates[user.currency], - network: "liquid" - }, - { transaction } - ); - } - - account = await db.Account.create( - { - asset: token, - user_id: invoice.user_id, - domain, - ticker: `${ticker}REISSUANCETOKEN`, - precision: 8, - name: `${name} Reissuance Token`, - balance: 0, - pending: address ? 0 : Math.round(token_amount * SATS) - }, - { transaction } - ); - emit(recipient.username, "account", account); - - const token_payment = await db.Payment.create( - { - account_id: account.id, - invoice_id: invoice.id, - user_id, - hash: txid, - amount: Math.round(token_amount * SATS), - received: true, - confirmed: false, - address: token_address, - network: "liquid" - }, - { transaction } - ); - emit(recipient.username, "payment", token_payment); - - if (invoice.user_id !== user.id) { - const token_send = await db.Payment.create( - { - account_id: account.id, - invoice_id: invoice.id, - user_id, - hash: txid, - amount: -Math.round(token_amount * SATS), - received: false, - confirmed: false, - address, - network: "liquid" - }, - { transaction } - ); - emit(recipient.username, "payment", token_send); - } - - store.issuances[txid].token = token; - store.issuances[txid].token_amount = token_amount; - store.issuances[txid].token_payment_id = token_payment.id; - } - }); - - res.send(store.issuances[txid] ? store.issuances[txid] : { asset }); - } catch (e) { - err("asset issuance failed", e.message, e.stack); - res.code(500).send(e.message); - } -}); - -app.post("/assets/register", auth, async (req, res) => { - const { asset } = req.body; - const account = await db.Account.findOne({ - where: { - user_id: req.user.id, - asset - } - }); - - l("registering", asset, account.contract); - - try { - const { data: result } = await axios.post( - "https://assets.blockstream.info/", - { - asset_id: asset, - contract: account.contract - } - ); - l("register asset result", req.user.username, result); - res.send(result); - } catch (e) { - err("asset registration failed", e.message); - res.code(500).send(e.message); - } -}); - -app.post("/loadFaucet", auth, async (req, res) => { - return res.code(500).send("Faucet feature temporarily disabled"); - - const { user } = req; - let { asset, amount } = req.body; - amount = parseInt(amount); - - try { - if (amount <= 0) throw new Error("Amount to load cannot be negative"); - - await db.transaction(async transaction => { - let account = await getAccount(config.liquid.btcasset, user, transaction); - - if (account.asset !== config.liquid.btcasset) - throw new Error( - "Faucet has to be funded with bitcoin. Try sending from another wallet." - ); - if (account.balance < amount) throw new Error("Insufficient funds"); - - let fee = 0; - - await account.decrement({ balance: amount }, { transaction }); - await account.reload({ transaction }); - - let a2; - let acc = { - user_id: null, - asset - }; - - a2 = await db.Account.findOne({ - where: acc, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (a2) { - await a2.increment({ balance: amount }, { transaction }); - await a2.reload({ transaction }); - } else { - let name = asset.substr(0, 6); - let domain; - let ticker = asset.substr(0, 3).toUpperCase(); - let precision = 8; - - const assets = assets; - - if (assets[asset]) { - ({ domain, ticker, precision, name } = assets[asset]); - } else { - const existing = await db.Account.findOne({ - where: { - asset - }, - order: [["id", "ASC"]], - limit: 1, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (existing) { - ({ domain, ticker, precision, name } = existing); - } - } - - acc = { ...acc, ...{ domain, ticker, precision, name } }; - acc.balance = amount; - acc.pending = 0; - acc.network = "liquid"; - a2 = await db.Account.create(acc, { transaction }); - } - - let params = { - amount: -amount, - account_id: account.id, - user_id: user.id, - rate: store.rates[user.currency], - currency: user.currency, - confirmed: true, - hash: `Loaded Faucet - ${a2.ticker}`, - network: "COINOS" - }; - - let payment = await db.Payment.create(params, { transaction }); - - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); - - l("sent internal", user.username, -payment.amount); - - emit(user.username, "payment", payment); - emit(user.username, "account", account); - emit(user.username, "user", user); - - l("loaded faucet", asset, amount); - res.send({}); - }); - } catch (e) { - err("problem loading faucet", user.username, e.message); - return res.code(500).send(e.message); - } -}); - -app.get("/faucet", auth, async (req, res) => { - let { asset } = req.query; - let faucet = await db.Account.findOne({ - where: { - asset, - user_id: null - } - }); - - if (!faucet) - faucet = { - asset, - balance: 0, - ticker: "Unknown" - }; - - res.send(faucet); -}); diff --git a/routes/bitcoin/broadcast.js b/routes/bitcoin/broadcast.js deleted file mode 100644 index 85c8cf66..00000000 --- a/routes/bitcoin/broadcast.js +++ /dev/null @@ -1,56 +0,0 @@ -import { emit } from "$lib/sockets"; -import store from "$lib/store"; -import { requirePin } from "$lib/utils"; - -export default async (req, res) => { - try { - await requirePin(req); - - const { user } = req; - let hash = await bc.sendRawTransaction(req.body.tx); - - if (user) { - await db.transaction(async transaction => { - let account = await db.Account.findOne({ - where: { - id: user.account_id - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - let payment = await db.Payment.create( - { - ...req.body.payment, - hash, - amount: -req.body.payment.amount, - tip: 0, - account_id: account.id, - user_id: user.id, - rate: store.rates[user.currency], - currency: user.currency, - confirmed: true, - received: false, - network: "bitcoin" - }, - { transaction } - ); - - await account.increment( - { balance: payment.amount - payment.fee }, - { 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", payment.account); - res.send(payment); - }); - } else { - res.send(result); - } - } catch (e) { - res.code(500).send(e.message); - } -}; diff --git a/routes/bitcoin/conversionFee.js b/routes/bitcoin/conversionFee.js deleted file mode 100644 index 7e305659..00000000 --- a/routes/bitcoin/conversionFee.js +++ /dev/null @@ -1,12 +0,0 @@ -// Code for the conversion fee -// This is in a separate file so that the same function can be used by ./send and ./receive - -const conversionFeeMultiplier = 0.001; -export const conversionFeeReceiver = "coinosfees"; // account that receives the fees - -/** - * Returns the fee you have to pay for withdrawing 'amount' bitcoin. - */ -export function computeConversionFee(amount) { - return Math.floor(amount * conversionFeeMultiplier); -} diff --git a/routes/bitcoin/fee.js b/routes/bitcoin/fee.js deleted file mode 100644 index 74c26a92..00000000 --- a/routes/bitcoin/fee.js +++ /dev/null @@ -1,73 +0,0 @@ -import config from "$config"; -import db from "$db"; -import bc from "$lib/bitcoin"; -import { err } from "$lib/logging"; -import { emit } from "$lib/sockets"; -import buildTx from "$lib/buildtx"; -import { SATS } from "$lib/utils"; - -export default async (req, res) => { - let { user } = req; - let { address, amount, feeRate, replaceable = true } = req.body; - let tx, fee; - - if (user.account.pubkey) { - try { - let psbt = await buildTx({ address, amount, feeRate, replaceable, user }); - return res.send(psbt); - } catch (e) { - return res.code(500).send(e.message); - } - } - - let invoice = await db.Invoice.findOne({ - where: { address }, - include: { - attributes: ["username"], - model: db.User, - as: "user" - } - }); - - if (invoice) { - let { ismine } = await bc.getAddressInfo(address); - if (ismine) { - emit(user.username, "to", invoice.user); - return res.send({}); - } - } - - try { - amount = parseInt(amount); - - let partial = await bc.createRawTransaction( - [], - { - [address]: (amount / SATS).toFixed(8) - }, - 0, - replaceable - ); - - let params = { - subtractFeeFromOutputs: amount === user.balance ? [0] : [], - replaceable - }; - - if (feeRate) params.feeRate = (feeRate / SATS).toFixed(8); - - tx = await bc.fundRawTransaction(partial, params); - - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); - - let signed = await bc.signRawTransactionWithWallet(tx.hex); - let decoded = await bc.decodeRawTransaction(signed.hex); - feeRate = Math.round((tx.fee * SATS * 1000) / decoded.vsize); - - res.send({ feeRate, tx }); - } catch (e) { - err("bitcoin fee estimation error", e); - return res.code(500).send(e.message); - } -}; diff --git a/routes/bitcoin/generate.js b/routes/bitcoin/generate.js deleted file mode 100644 index 6285492d..00000000 --- a/routes/bitcoin/generate.js +++ /dev/null @@ -1,5 +0,0 @@ -export default async (req, res) => { - let address = await bc.getNewAddress(); - await bc.generateToAddress(1, address); - res.send({}); -}; diff --git a/routes/bitcoin/index.js b/routes/bitcoin/index.js deleted file mode 100644 index 58b0d193..00000000 --- a/routes/bitcoin/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import broadcast from "./broadcast"; -import generate from "./generate"; -import sweep from "./sweep"; -import fee from "./fee"; -import send from "./send"; - -export default { - broadcast, - generate, - sweep, - fee, - send -}; diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js deleted file mode 100644 index 10193ceb..00000000 --- a/routes/bitcoin/receive.js +++ /dev/null @@ -1,281 +0,0 @@ -import config from "$config"; -import db from "$db"; -import bc from "$lib/bitcoin"; -import { emit } from "$lib/sockets"; -import store from "$lib/store"; -import { notify } from "$lib/notifications"; -import { callWebhook } from "$lib/webhooks"; -import reverse from "buffer-reverse"; -import zmq from "zeromq/v5-compat"; -import { Op } from "@sequelize/core"; -import { fromBase58 } from "bip32"; -import bitcoin from "bitcoinjs-lib"; -import { sendLiquid } from "$routes/liquid/send"; -import { computeConversionFee } from "./conversionFee"; -import { l, warn, err } from "$lib/logging"; - -const zmqRawBlock = zmq.socket("sub"); -zmqRawBlock.connect(config.bitcoin.zmqrawblock); -zmqRawBlock.subscribe("rawblock"); - -const zmqRawTx = zmq.socket("sub"); -zmqRawTx.connect(config.bitcoin.zmqrawtx); -zmqRawTx.subscribe("rawtx"); - -const network = - bitcoin.networks[ - config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network - ]; - -const queue = {}; -const seen = []; - -console.warn = () => {}; - -zmqRawTx.on("message", async (topic, message, sequence) => { - try { - const hex = message.toString("hex"); - let tx = bitcoin.Transaction.fromHex(message); - - let hash = reverse(tx.getHash()).toString("hex"); - - if (seen.includes(hash)) return; - seen.push(hash); - if (seen.length > 5000) seen.shift(); - - Promise.all( - tx.outs.map(async o => { - try { - const { value } = o; - - let address; - try { - address = bitcoin.address.fromOutputScript(o.script, network); - } catch (e) { - return; - } - - if ( - Object.keys(store.addresses).includes(address) && - !store.change.includes(address) - ) { - await db.transaction(async transaction => { - let user = await db.User.findOne({ - where: { - username: store.addresses[address] - }, - transaction - }); - - let invoice = await db.Invoice.findOne({ - where: { - address, - user_id: user.id, - network: "bitcoin" - }, - order: [["id", "DESC"]], - include: { - model: db.Account, - as: "account" - }, - transaction - }); - - if (!invoice) return; - - const currency = invoice ? invoice.currency : user.currency; - const rate = invoice ? invoice.rate : store.rates[user.currency]; - const tip = invoice ? invoice.tip : 0; - const memo = invoice ? invoice.memo : ""; - - let confirmed = false; - - let { account } = invoice; - - if (account.asset !== config.liquid.btcasset) { - account = await db.Account.findOne({ - where: { - user_id: user.id, - asset: config.liquid.btcasset, - pubkey: null - }, - transaction - }); - } - - await invoice.increment({ pending: value }, { transaction }); - await invoice.reload({ transaction }); - - await account.increment({ pending: value }, { transaction }); - await account.reload({ transaction }); - - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); - - let totalOutputs = tx.outs.reduce((a, b) => a + b.value, 0); - let totalInputs = 0; - for (let i = 0; i < tx.ins.length; i++) { - let { hash, index } = tx.ins[i]; - hash = reverse(hash).toString("hex"); - let hex = await bc.getRawTransaction(hash); - let inputTx = bitcoin.Transaction.fromHex(hex); - totalInputs += inputTx.outs[index].value; - } - let fee = totalInputs - totalOutputs; - - let payment = await db.Payment.create( - { - account_id: account.id, - user_id: user.id, - hash, - fee, - memo, - amount: value - tip, - currency, - rate, - received: true, - tip, - confirmed, - address, - network: "bitcoin", - invoice_id: invoice.id - }, - { transaction } - ); - - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); - payment.invoice = invoice.get({ plain: true }); - - emit(user.username, "account", account); - emit(user.username, "payment", payment); - l("bitcoin detected", user.username, value); - notify(user, `${value} SAT payment detected`); - callWebhook(invoice, payment); - }); - } - } catch (e) { - console.log("problem parsing bitcoin tx", e); - } - }) - ); - } catch (e) { - console.log("problem receiving bitcoin tx", e); - } -}); - -zmqRawBlock.on("message", async (topic, message, sequence) => { - try { - const payments = await db.Payment.findAll({ - where: { confirmed: false } - }); - - let block = bitcoin.Block.fromHex(message.toString("hex")); - block.transactions.map(tx => { - let hash = reverse(tx.getHash()).toString("hex"); - if (payments.find(p => p.hash === hash)) queue[hash] = 1; - }); - } catch (e) { - console.log(e); - } -}); - -setInterval(async () => { - try { - const arr = Object.keys(queue); - for (let i = 0; i < arr.length; i++) { - const hash = arr[i]; - - let account, address, invoice, user, total; - await db.transaction(async transaction => { - let payments = await db.Payment.findAll({ - where: { hash, confirmed: 0, received: 1 }, - include: [ - { - model: db.Account, - as: "account" - }, - { - model: db.Invoice, - as: "invoice" - }, - { - model: db.User, - as: "user" - } - ], - lock: transaction.LOCK.UPDATE, - transaction - }); - - for (let i = 0; i < payments.length; i++) { - let p = payments[i]; - if (p && p.address) address = p.address; - if (p && p.user) user = p.user; - - if (p && p.account) { - ({ account, invoice } = p); - total = p.amount + p.tip; - - p.confirmed = 1; - await account.save({ transaction }); - - await invoice.increment({ received: total }, { transaction }); - await invoice.decrement( - { pending: Math.min(invoice.pending, total) }, - { transaction } - ); - - await account.increment({ balance: total }, { transaction }); - await account.decrement( - { pending: Math.min(account.pending, total) }, - { transaction } - ); - // get the # of fee credits you would need to pay off this amount of bitcoin - await account.increment( - { btc_credits: computeConversionFee(total) }, - { transaction } - ); - await account.reload({ transaction }); - await invoice.reload({ transaction }); - await p.save({ transaction }); - - p = p.get({ plain: true }); - p.account = account.get({ plain: true }); - p.invoice = invoice.get({ plain: true }); - - emit(user.username, "account", account); - emit(user.username, "payment", p); - l("bitcoin confirmed", user.username, p.amount, p.tip); - notify(user, `${total} SAT payment confirmed`); - callWebhook(p.invoice, p); - - let c = store.convert[address]; - if (address && c) { - l( - "bitcoin detected to conversion address", - address, - c.address, - user.username - ); - user.account = account; - - sendLiquid({ - address: c.address, - amount: total - 100, - user, - limit: total - }); - } - } else { - warn("couldn't find bitcoin payment", hash); - } - } - - delete queue[hash]; - }); - } - } catch (e) { - err("problem processing queued bitcoin transaction", e.message, e.stack); - } -}, 1000); diff --git a/routes/bitcoin/send.js b/routes/bitcoin/send.js deleted file mode 100644 index 3e030413..00000000 --- a/routes/bitcoin/send.js +++ /dev/null @@ -1,192 +0,0 @@ -import bc from "$lib/bitcoin"; -import { emit } from "$lib/sockets"; -import db from "$db"; -import { toSats } from "$lib/utils"; -import store from "$lib/store"; -import config from "$config"; -import { l, warn, err } from "$lib/logging"; -import { requirePin } from "$lib/utils"; - -const btc = config.liquid.btcasset; -import { - computeConversionFee, - conversionFeeReceiver, -} from "./conversionFee.js"; - -export default async (req, res) => { - try { - await requirePin(req); - - let { user } = req; - let { address, memo, tx } = req.body; - let { hex } = tx; - - let fee = toSats(tx.fee); - if (fee < 0) throw new Error("fee cannot be negative"); - - const isChange = async ({ address }) => - (await bc.getAddressInfo(address)).ismine && - !Object.keys(store.addresses).includes(address); - - tx = await bc.decodeRawTransaction(hex); - - let total = 0; - let change = 0; - - for (let i = 0; i < tx.vout.length; i++) { - let o = tx.vout[i]; - total += toSats(o.value); - - if (await isChange(o.scriptPubKey)) { - change += toSats(o.value); - } - } - - total = total - change + fee; - let amount = total - fee; - - // get conversion fee - // 'total' refers to the total before the conversion fee - // (i.e. the total bitcoin that leaves this server) - let conversionFee = computeConversionFee(amount); - - // withdraw bitcoin - await db.transaction(async (transaction) => { - let { account } = user; - if (account.asset !== btc) { - account = await db.Account.findOne({ - where: { - user_id: user.id, - asset: btc, - pubkey: null, - }, - lock: transaction.LOCK.UPDATE, - transaction, - }); - } - - // use user's credits to reduce fee, if available - let conversionFeeDeduction = Math.min(account.btc_credits, conversionFee); - if (conversionFeeDeduction) { - await account.decrement( - { btc_credits: conversionFeeDeduction }, - { transaction } - ); - await account.reload({ transaction }); - conversionFee -= conversionFeeDeduction; - } - - l( - "attempting to send bitcoin", - user.username, - account.id, - address, - total, - conversionFee, - conversionFeeDeduction - ); - - if (total > account.balance) { - err("amount exceeds balance", amount, fee, account.balance); - throw new Error("low"); - } else if (total + conversionFee > account.balance) { - err( - "total (after conversion fee) exceeds balance", - amount, - fee, - account.balance - ); - throw new Error("low"); - } - - await account.decrement( - { balance: total + conversionFee }, - { transaction } - ); - await account.reload({ transaction }); - - let receiverAccount = await db.Account.findOne({ - where: { - "$user.username$": conversionFeeReceiver, - }, - include: [ - { - model: db.User, - as: "user", - }, - ], - lock: transaction.LOCK.UPDATE, - transaction, - }); - - let fee_payment; - let fee_payment_id = null; - if (conversionFee) { - await receiverAccount.increment( - { balance: conversionFee }, - { transaction } - ); - await receiverAccount.reload({ transaction }); - fee_payment = await db.Payment.create( - { - amount: conversionFee, - fee: 0, - memo: "Bitcoin conversion fee", - account_id: receiverAccount.id, - user_id: receiverAccount.user_id, - rate: store.rates[receiverAccount.user.currency], - currency: receiverAccount.user.currency, - confirmed: true, - received: true, - network: "COINOS", - }, - { transaction } - ); - fee_payment_id = fee_payment.id; - } - - // record the external bitcoin transaction - let params = { - amount: -amount, - fee, - memo, - account_id: account.id, - user_id: user.id, - rate: store.rates[user.currency], - currency: user.currency, - address, - confirmed: true, - received: false, - network: "bitcoin", - fee_payment_id: fee_payment_id, - }; - - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); - - hex = (await bc.signRawTransactionWithWallet(hex)).hex; - params.hash = await bc.sendRawTransaction(hex); - - let payment = await db.Payment.create(params, { transaction }); - - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); - payment.fee_payment = fee_payment && fee_payment.get({ plain: true }); - - emit(user.username, "payment", payment); - res.send(payment); - - store.payments.push(params.hash); - l("sent bitcoin", user.username, total); - }); - } catch (e) { - if (e.message.includes("Insufficient")) - e.message = - "Our server has insufficient funds, try again later"; - - if (e.message === "low") e.message = "Insufficient funds in account" - - err("error sending bitcoin", e.message); - return res.code(500).send(e.message); - } -}; diff --git a/routes/bitcoin/sweep.js b/routes/bitcoin/sweep.js deleted file mode 100644 index 49fb1f04..00000000 --- a/routes/bitcoin/sweep.js +++ /dev/null @@ -1,87 +0,0 @@ -import config from "$config"; -import { prod } from "$lib/utils"; -import axios from "axios"; -import bitcoin from "bitcoinjs-lib"; -import coinselect from "coinselect"; -import split from "coinselect/split"; - -const api = prod ? "https://blockstream.info/api" : config.bitcoin.electrs; -const SATS = 100000000; -const network = prod - ? bitcoin.networks["bitcoin"] - : bitcoin.networks["regtest"]; - -export default async (req, res) => { - let { address: from, amount, feeRate, target } = req.body; - let utxos; - if (!feeRate) feeRate = (await bc.estimateSmartFee(6)).feerate * SATS; - feeRate = Math.round(feeRate / 1000); - l("sweeping", req.user.username, from, amount, feeRate); - - try { - let { - data: { - chain_stats: { funded_txo_sum: funded, spent_txo_sum: spent } - } - } = await axios.get(`${api}/address/${from}`); - let balance = funded - spent; - console.log("boom"); - - try { - console.log("fetching utxo"); - ({ data: utxos } = await axios.get(`${api}/address/${from}/utxo`)); - console.log("got utxos", utxos); - } catch (e) { - err("problem fetching utxos", e.message); - } - - let targets = [ - { - address: target, - value: amount - } - ]; - - let { inputs, outputs, fee } = coinselect(utxos, targets, feeRate); - - if (balance === amount) { - delete targets[0].value; - ({ inputs, outputs, fee } = split(utxos, targets, feeRate)); - l("split", inputs, outputs, fee); - } - - if (!inputs || !outputs) - return res - .code(500) - .send("Unable to construct sweep transaction, try a lower fee rate?"); - - let psbt = new bitcoin.Psbt({ network }); - let total = 0; - - outputs.map(({ address, value }) => { - total += value; - psbt = psbt.addOutput({ - address: address || from, - value - }); - }); - - for (let i = 0; i < inputs.length; i++) { - let { txid, vout } = inputs[i]; - let nonWitnessUtxo = Buffer.from(await bc.getRawTransaction(txid), "hex"); - psbt = psbt.addInput({ - hash: txid, - index: vout, - nonWitnessUtxo - }); - } - - feeRate = Math.round(feeRate * 1000); - psbt = psbt.toBase64(); - - if (balance !== amount) total = 0; - res.send({ feeRate, psbt, total }); - } catch (e) { - err("problem getting address stats", from, e.message); - } -}; diff --git a/routes/events.js b/routes/events.js index e52b8a7c..c105dc40 100644 --- a/routes/events.js +++ b/routes/events.js @@ -5,72 +5,3 @@ import { coinos, pool, q } from "$lib/nostr"; import store from "$lib/store"; import { nada, wait } from "$lib/utils"; -app.get("/event/:id", async (req, res) => { - try { - let event = JSON.parse(await redis.get(`ev:${req.params.id}`)); - let { pubkey } = event; - - event.user = JSON.parse(await redis.get(`user:${pubkey}`)) || { - username: pubkey.substr(0, 6), - pubkey, - anon: true, - follows: [], - followers: [] - }; - - res.send(event); - } catch (e) { - res.code(500).send("Problem fetching event"); - } -}); - -app.get("/nostr/:pubkey", async (req, res) => { - try { - let { pubkey } = req.params; - - let user = JSON.parse(await redis.get(`user:${pubkey}`)); - - if (!user) { - user = await db.User.findOne({ - where: { pubkey } - }); - } - - if (!user) - user = { - username: pubkey.substr(0, 6), - pubkey, - anon: true, - follows: [], - followers: [] - }; - - q( - `${pubkey}:notes`, - { - kinds: [1], - authors: [pubkey] - }, - { since: 0 } - ).catch(nada); - - await redis.set(`user:${pubkey}`, JSON.stringify(user)); - - let ids = await redis.sMembers(pubkey); - - let events = ids.length - ? (await redis.mGet(ids.map(k => "ev:" + k))).map(JSON.parse) - : []; - - res.send(events.map(e => ({ ...e, user }))); - } catch (e) { - console.log(e); - res.code(500).send("problem fetching user events"); - } -}); - -app.post("/nostr/send", async (req, res) => { - let { event } = req.body; - pool.send(["EVENT", event]); - res.send(event); -}); diff --git a/routes/funding.js b/routes/funding.js deleted file mode 100644 index e65a704e..00000000 --- a/routes/funding.js +++ /dev/null @@ -1,96 +0,0 @@ -import { emit } from "$lib/sockets"; -import app from "$app"; -import config from "$config"; -import mailgunFactory from "mailgun-js"; -const mailgun = mailgunFactory(config.mailgun); - -app.post("/id", auth, upload.single("id"), async (req, res) => { - const { user } = req; - if (user.verified === "proof") user.verified = "pending"; - else user.verified = "id"; - await user.save(); - emit(user.username, "user", user); - - let data = { - subject: "KYC Documents", - text: `ID uploaded for ${user.username}`, - from: "server@coinos.io", - to: "kyc@coinos.io" - }; - - mailgun.messages().send(data); - l("id uploaded", user.username); - - res.send({}); -}); - -app.post("/proof", auth, upload.single("proof"), async (req, res) => { - const { user } = req; - if (user.verified === "id") user.verified = "pending"; - else user.verified = "proof"; - await user.save(); - emit(user.username, "user", user); - l("id proof uploaded", user.username); - - res.send({}); -}); - -app.post("/funding", auth, async (req, res) => { - try { - let { id, amount, code } = req.body; - - if (!code) { - code = ""; - let d = "ABCDEFGHIJKLMNPQRSTUVWXYZ23456789"; - let l = d.length; - for (let i = 0; i < 8; i++) { - code += d.charAt(Math.floor(Math.random() * l)); - } - } - - let params = { - user_id: req.user.id, - amount, - code - }; - - let deposit; - if (id) { - if (!parseFloat(amount) || parseFloat(amount) < 0) - throw new Error("Invalid amount"); - deposit = await db.Deposit.findOne({ where: { id } }); - deposit.update(params); - await deposit.save(); - } else { - deposit = await db.Deposit.create(params); - } - - res.send(deposit); - } catch (e) { - err("funding error", e.message); - res.code(500).send("Funding request failed"); - } -}); - -app.post("/withdrawal", auth, async (req, res) => { - try { - const { account, amount, email, institution, transit, notes } = req.body; - if (!parseFloat(amount) || parseFloat(amount) < 0) - throw new Error("Invalid amount"); - - await db.Withdrawal.create({ - user_id: req.user.id, - account, - amount, - email, - institution, - transit, - notes - }); - - res.send({}); - } catch (e) { - err("withdrawaerr", e.message); - res.code(500).send("Withdrawal request failed"); - } -}); diff --git a/routes/invoices.js b/routes/invoices.js index 338c66e7..f3c95a21 100644 --- a/routes/invoices.js +++ b/routes/invoices.js @@ -1,13 +1,8 @@ import app from "$app"; -import db from "$db"; import store from "$lib/store"; import { optionalAuth } from "$lib/passport"; -import { Op } from "@sequelize/core"; -import axios from "axios"; import { err, l } from "$lib/logging"; -import bc from "$lib/bitcoin"; -import lq from "$lib/liquid"; -import { SATS, bip21, deriveAddress, derivePayRequest } from "$lib/utils"; +import { SATS, derivePayRequest } from "$lib/utils"; import { emit } from "$lib/sockets"; app.get("/invoice", async (req, res, next) => { diff --git a/routes/lightning/query.js b/routes/lightning/query.js index 1c217a08..c5456a6f 100644 --- a/routes/lightning/query.js +++ b/routes/lightning/query.js @@ -1,4 +1,3 @@ -import db from "$db"; import { emit } from "$lib/sockets"; export default async (req, res) => { diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index ef973acc..a778271b 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -1,11 +1,9 @@ -import db from "$db"; import store from "$lib/store"; import { emit } from "$lib/sockets"; import config from "$config"; import { notify } from "$lib/notifications"; import { callWebhook } from "$lib/webhooks"; import { computeConversionFee } from "./conversionFee"; -import { sendLiquid } from "$routes/liquid/send"; import ln from "$lib/ln"; import { l, err, warn } from "$lib/logging"; @@ -101,29 +99,6 @@ const handlePayment = async msg => { payment.tip ); }); - - let c = store.convert[msg.request]; - if (msg.request && c) { - l( - "lightning detected for conversion request", - msg.request, - c.address, - user.username - ); - - user.account = account; - - try { - sendLiquid({ - address: c.address, - amount: total - 100, - user, - limit: total - }); - } catch (e) { - err("problem sending liquid payment", e.message, e.stack); - } - } } catch (e) { console.log(e); err("problem receiving lightning payment", e.message); diff --git a/routes/liquid/broadcast.js b/routes/liquid/broadcast.js deleted file mode 100644 index 820c0be1..00000000 --- a/routes/liquid/broadcast.js +++ /dev/null @@ -1,58 +0,0 @@ -import { emit } from "$lib/sockets"; -import store from "$lib/store"; -import { requirePin } from "$lib/utils"; - -export default async (req, res) => { - try { - await requirePin(req); - - const { user } = req; - let hash = await lq.sendRawTransaction(req.body.tx); - - if (user) { - await db.transaction(async transaction => { - let account = await db.Account.findOne({ - where: { - id: user.account_id - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - let payment = await db.Payment.create( - { - ...req.body.payment, - hash, - amount: -req.body.payment.amount, - tip: 0, - account_id: account.id, - user_id: user.id, - rate: store.rates[user.currency], - currency: user.currency, - confirmed: true, - received: false, - network: "liquid" - }, - { transaction } - ); - - await account.increment( - { balance: payment.amount - payment.fee }, - { 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", payment.account); - res.send(payment); - }); - } else { - res.send(hash); - } - } catch (e) { - err("problem broadcasting liquid transaction", e); - res.code(500).send(e.message); - } -}; diff --git a/routes/liquid/conversionFee.js b/routes/liquid/conversionFee.js deleted file mode 100644 index a25c2e7d..00000000 --- a/routes/liquid/conversionFee.js +++ /dev/null @@ -1,12 +0,0 @@ -// Code for the conversion fee -// This is in a separate file so that the same function can be used by ./send and ./receive - -const conversionFeeMultiplier = 0.001; -export const conversionFeeReceiver = "coinosfees"; // account that receives the fees - -/** - * Returns the fee you have to pay for withdrawing 'amount' L-BTC. - */ -export function computeConversionFee(amount) { - return Math.floor(amount * conversionFeeMultiplier); -} diff --git a/routes/liquid/fee.js b/routes/liquid/fee.js deleted file mode 100644 index ae6e4393..00000000 --- a/routes/liquid/fee.js +++ /dev/null @@ -1,89 +0,0 @@ -import config from "$config"; -import db from "$db"; -import { emit } from "$lib/sockets"; -import buildTx from "$lib/buildliquidtx"; -import { err, l } from "$lib/logging"; -import { SATS } from "$lib/utils"; -import lq from "$lib/liquid"; - -export const liquidTx = async ({ address, asset, amount, feeRate, replaceable, user }) => { - let tx, fee; - - if (user.account.pubkey) { - let psbt = await buildTx({ - address, - asset, - amount, - feeRate, - replaceable, - user - }); - return psbt; - } - - let invoice = await db.Invoice.findOne({ - where: { address }, - include: { - attributes: ["username"], - model: db.User, - as: "user" - } - }); - - if (invoice) { - let { ismine } = await lq.getAddressInfo(address); - if (ismine) { - emit(user.username, "to", invoice.user); - return; - } - } - - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); - - amount = parseInt(amount); - - let params = { - subtractFeeFromOutputs: amount === user.balance ? [0] : [] - }; - - let value = (amount / SATS).toFixed(8); - if (feeRate) params.feeRate = (feeRate / SATS).toFixed(8); - - let info = await lq.getAddressInfo(address); - - tx = await lq.createRawTransaction( - [], - [ - { - [address]: (amount / SATS).toFixed(8), - asset - } - ], - 0, - replaceable - ); - - l("funding tx for fee estimate", tx); - - tx = await lq.fundRawTransaction(tx, params); - - let blinded = await lq.blindRawTransaction(tx.hex); - let signed = await lq.signRawTransactionWithWallet(blinded); - - let decoded = await lq.decodeRawTransaction(signed.hex); - feeRate = Math.round((tx.fee * SATS * 1000) / decoded.vsize); - l("estimated", asset, feeRate); - - return { feeRate, tx }; -}; - -export default async (req, res) => { - try { - let tx = await liquidTx({ ...req.body, user: req.user }); - res.send(tx); - } catch (e) { - err("error estimating liquid fee", e.message, e.stack); - return res.code(500).send(e.message); - } -}; diff --git a/routes/liquid/generate.js b/routes/liquid/generate.js deleted file mode 100644 index 423d8b6a..00000000 --- a/routes/liquid/generate.js +++ /dev/null @@ -1,5 +0,0 @@ -export default async (req, res) => { - let address = await lq.getNewAddress(); - await lq.generateToAddress(1, address); - res.send({}); -}; diff --git a/routes/liquid/index.js b/routes/liquid/index.js deleted file mode 100644 index c4c50021..00000000 --- a/routes/liquid/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import broadcast from "./broadcast"; -import generate from "./generate"; -import fee from "./fee"; -import send from "./send"; - -export default { - broadcast, - generate, - fee, - send, -}; diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js deleted file mode 100644 index 718feba5..00000000 --- a/routes/liquid/receive.js +++ /dev/null @@ -1,406 +0,0 @@ -import db from "$db"; -import config from "$config"; -import { getBlockHeight, getUser, getUserById, toSats, SATS } from "$lib/utils"; -import { emit } from "$lib/sockets"; -import store from "$lib/store"; -import { sendLiquid } from "$routes/liquid/send"; -import { notify } from "$lib/notifications"; -import { callWebhook } from "$lib/webhooks"; -import reverse from "buffer-reverse"; -import zmq from "zeromq/v5-compat"; -import { Op } from "@sequelize/core"; -import { fromBase58 } from "bip32"; -import bitcoin from "bitcoinjs-lib"; -import liquidJs from "liquidjs-lib"; -import { l, err, warn } from "$lib/logging"; -import lq from "$lib/liquid"; - -const { Block, networks, Transaction } = liquidJs; - -const network = - networks[ - config.liquid.network === "mainnet" ? "liquid" : config.liquid.network - ]; - -import { computeConversionFee } from "./conversionFee"; - -const getAccount = async (params, transaction) => { - let account = await db.Account.findOne({ - where: params, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (account) { - l("found account", params, account.asset, account.id); - return account; - } - - let { asset, pubkey } = params; - let name = asset.substr(0, 6); - let domain = ""; - let ticker = asset.substr(0, 3).toUpperCase(); - let precision = 8; - - if (pubkey) { - const nc = await db.Account.findOne({ - where: { pubkey }, - order: [["id", "ASC"]], - limit: 1, - lock: transaction.LOCK.UPDATE, - transaction - }); - - params.index = 0; - params.path = nc.path; - params.pubkey = nc.pubkey; - params.privkey = nc.privkey; - params.seed = nc.seed; - } - - if (store.assets[asset]) { - ({ domain, ticker, precision, name } = store.assets[asset]); - } else { - const existing = await db.Account.findOne({ - where: { - asset - }, - order: [["id", "ASC"]], - limit: 1, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (existing) { - l("existing", existing.id); - ({ domain, ticker, precision, name } = existing); - } - } - - params = { ...params, ...{ domain, ticker, precision, name } }; - params.balance = 0; - params.pending = 0; - params.network = "liquid"; - return db.Account.create(params, { transaction }); -}; - -const zmqRawBlock = zmq.socket("sub"); -zmqRawBlock.connect(config.liquid.zmqrawblock); -zmqRawBlock.subscribe("rawblock"); - -const zmqRawTx = zmq.socket("sub"); -zmqRawTx.connect(config.liquid.zmqrawtx); -zmqRawTx.subscribe("rawtx"); - -const queue = {}; -const seen = []; - -zmqRawTx.on("message", async (topic, message, sequence) => { - try { - const hex = message.toString("hex"); - - let unblinded, tx, blinded, txid; - try { - txid = Transaction.fromHex(hex).getId(); - if (seen.includes(txid)) return; - seen.push(txid); - if (seen.length > 5000) seen.shift(); - - unblinded = await lq.unblindRawTransaction(hex); - tx = await lq.decodeRawTransaction(unblinded.hex); - blinded = await lq.decodeRawTransaction(hex); - } catch (e) { - return err("problem decoding liquid tx", e.message); - } - - for (let o of tx.vout) { - try { - if (!(o.scriptPubKey && o.scriptPubKey.address)) return; - - const { asset } = o; - const value = toSats(o.value); - const { address } = o.scriptPubKey; - - if ( - Object.keys(store.addresses).includes(address) && - !store.change.includes(address) - ) { - await db.transaction(async transaction => { - let user = await getUser(store.addresses[address], transaction); - - let invoice = await db.Invoice.findOne({ - where: { - [Op.or]: { - unconfidential: address, - address - }, - user_id: user.id, - network: "liquid" - }, - order: [["id", "DESC"]], - transaction - }); - - if (!invoice) return; - - let confirmed = 0; - - let account = await db.Account.findOne({ - where: { - id: invoice.account_id, - asset - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (!account) { - let pubkey = account ? account.pubkey : null; - - account = await getAccount( - { - user_id: user.id, - asset, - pubkey - }, - transaction - ); - } - - await account.increment({ pending: value }, { transaction }); - await account.reload({ transaction }); - - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); - - await user.save({ transaction }); - - const currency = invoice ? invoice.currency : user.currency; - const rate = invoice ? invoice.rate : store.rates[user.currency]; - const tip = invoice ? invoice.tip : 0; - const memo = invoice ? invoice.memo : ""; - - let payment = await db.Payment.create( - { - account_id: account.id, - user_id: user.id, - hash: blinded.txid, - amount: value - tip, - currency, - memo, - rate, - received: true, - tip, - confirmed, - address, - network: "liquid", - invoice_id: invoice.id - }, - { transaction } - ); - - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); - payment.invoice = invoice.get({ plain: true }); - - emit(user.username, "payment", payment); - emit(user.username, "account", payment.account); - l("liquid detected", address, user.username, asset, value); - notify(user, `${value} SAT payment detected`); - callWebhook(invoice, payment); - }); - } - } catch (e) { - console.log(e); - err("Problem processing transaction", e.message, e.stack); - } - } - } catch (e) { - console.log(e); - } -}); - -zmqRawBlock.on("message", async (topic, message, sequence) => { - try { - const payments = await db.Payment.findAll({ - where: { confirmed: 0 } - }); - - let hash, json; - - hash = await lq.getBlockHash(getBlockHeight(message)); - json = await lq.getBlock(hash, 2); - - for (let tx of json.tx) { - if (store.issuances[tx.txid]) { - await db.transaction(async transaction => { - const { - user_id, - asset, - asset_amount, - asset_payment_id, - token, - token_amount, - token_payment_id - } = store.issuances[tx.txid]; - - const user = await getUserById(user_id); - - let account = await db.Account.findOne({ - where: { user_id, asset }, - lock: transaction.LOCK.UPDATE, - transaction - }); - account.balance = Math.round(asset_amount * SATS); - account.pending = 0; - await account.save({ transaction }); - - let payment = await db.Payment.findOne({ - where: { id: asset_payment_id }, - include: { - model: db.Account, - as: "account" - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - payment.confirmed = true; - await payment.save({ transaction }); - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); - - emit(user.username, "account", account); - emit(user.username, "payment", payment); - - if (token) { - account = await db.Account.findOne({ - where: { user_id, asset: token }, - lock: transaction.LOCK.UPDATE, - transaction - }); - account.balance = token_amount * SATS; - account.pending = 0; - await account.save({ transaction }); - - payment = await db.Payment.findOne({ - where: { id: token_payment_id }, - include: { - model: db.Account, - as: "account" - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - payment.confirmed = true; - await payment.save({ transaction }); - - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); - - emit(user.username, "account", account); - emit(user.username, "payment", payment); - } - }); - } else if (payments.find(p => p.hash === tx.txid)) queue[tx.txid] = 1; - } - } catch (e) { - return console.log(e); - } -}); - -setInterval(async () => { - try { - const arr = Object.keys(queue); - - for (let i = 0; i < arr.length; i++) { - const hash = arr[i]; - - let account, invoice, address, user, total, p; - await db.transaction(async transaction => { - p = await db.Payment.findOne({ - where: { hash, confirmed: 0, received: 1 }, - include: [ - { - model: db.Account, - as: "account" - }, - { - model: db.Invoice, - as: "invoice" - }, - { - model: db.User, - as: "user" - } - ], - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (p && p.address) address = p.address; - if (p && p.user) user = p.user; - - if (p && p.account) { - ({ account, invoice } = p); - total = p.amount + p.tip; - p.confirmed = 1; - await account.save({ transaction }); - - await invoice.increment({ received: total }, { transaction }); - await account.increment({ balance: total }, { transaction }); - await account.decrement( - { pending: Math.min(account.pending, total) }, - { transaction } - ); - await account.increment( - { liquid_credits: computeConversionFee(total) }, - { transaction } - ); - await account.reload({ transaction }); - await invoice.reload({ transaction }); - - await p.save({ transaction }); - - p = p.get({ plain: true }); - account = account.get({ plain: true }); - invoice = invoice.get({ plain: true }); - - emit(user.username, "account", account); - emit(user.username, "payment", p); - - l("liquid confirmed", user.username, account.asset, p.amount, p.tip); - - notify(user, `${total} SAT payment confirmed`); - callWebhook(p.invoice, p); - } else { - warn("couldn't find liquid payment", hash); - } - - delete queue[hash]; - }); - - let c = store.convert[address]; - if (address && c && p.account.asset === network.assetHash) { - l( - "liquid detected for conversion request", - address, - c.address, - user.username - ); - - user.account = p.account; - - sendLiquid({ - address: c.address, - amount: total - 100, - user, - limit: total - }); - } - } - } catch (e) { - err("problem processing queued liquid transaction", e.message, e.stack); - } -}, 1000); diff --git a/routes/liquid/send.js b/routes/liquid/send.js deleted file mode 100644 index 83b5d6ca..00000000 --- a/routes/liquid/send.js +++ /dev/null @@ -1,276 +0,0 @@ -import db from "$db"; -import lq from "$lib/liquid"; -import { toSats } from "$lib/utils"; -import { emit } from "$lib/sockets"; -import store from "$lib/store"; -import config from "$config"; -import { l, warn, err } from "$lib/logging"; -import { liquidTx } from "$routes/liquid/fee"; -import { Transaction } from 'liquidjs-lib'; -import { requirePin } from "$lib/utils"; - -import { - computeConversionFee, - conversionFeeReceiver -} from "./conversionFee.js"; - -const btc = config.liquid.btcasset; -const lcad = config.liquid.cadasset; - -export const sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { - try { - l("sending liquid", amount, address); - if (!tx) { - ({ tx } = await liquidTx({ - address, - asset, - amount, - feeRate: 200, - replaceable: false, - user - })); - } - - const isChange = async address => - (await lq.getAddressInfo(address)).ismine && - !Object.keys(store.addresses).includes(address); - - let totals = {}; - let change = {}; - let fee = 0; - - let { vout } = await lq.decodeRawTransaction(tx.hex); - - for (let i = 0; i < vout.length; i++) { - let { - asset, - value, - scriptPubKey - } = vout[i]; - - if (scriptPubKey.type === "fee") fee = toSats(value); - else { - if (!totals[asset]) totals[asset] = change[asset] = 0; - totals[asset] += toSats(value); - - if (scriptPubKey.address) { - if (await isChange(scriptPubKey.address)) { - change[asset] += toSats(value); - } - } - } - } - - const assets = Object.keys(totals); - const payments = []; - - let main, signed; - await db.transaction(async transaction => { - let fee_payment; - let fee_payment_id = null; - - for (let i = 0; i < assets.length; i++) { - let asset = assets[i]; - let amount = totals[asset]; - if (change[asset]) amount -= change[asset]; - let total = amount; - - if (asset === btc) { - let covered = 0; - let nonbtc = assets.filter(a => a !== btc); - if (nonbtc.length === 1) { - let faucet = await db.Account.findOne({ - where: { - asset: nonbtc[0], - user_id: null - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (faucet) { - covered = faucet.balance; - if (covered > fee) covered = fee; - await faucet.decrement({ balance: covered }, { transaction }); - await faucet.reload({ transaction }); - await faucet.save({ transaction }); - } - } - - total += fee - covered; - } - - var conversionFee = computeConversionFee(amount); - - if (limit && total > limit + fee) - throw new Error("Tx amount exceeds authorized amount"); - - if (asset !== btc || total) { - l("creating liquid payment", user.username, asset, total, fee); - - let account = await db.Account.findOne({ - where: { - user_id: user.id, - asset, - pubkey: null - }, - lock: transaction.LOCK.UPDATE, - order: [["balance", "DESC"]], - transaction - }); - - // use user's credits to reduce fee, if available - let conversionFeeDeduction = Math.min( - account.liquid_credits, - conversionFee - ); - if (conversionFeeDeduction) { - await account.decrement( - { liquid_credits: conversionFeeDeduction }, - { transaction } - ); - await account.reload({ transaction }); - conversionFee -= conversionFeeDeduction; - } - - if (asset !== btc) conversionFee = 0; - - if (total > account.balance) { - warn("amount exceeds balance", { - total, - fee, - balance: account.balance - }); - throw new Error( - `Insufficient funds, need ${total} ${ - account.ticker === "BTC" ? "SAT" : account.ticker - }, have ${account.balance}` - ); - } else if (total + conversionFee > account.balance) { - warn("amount plus conversion fee exceeds balance", { - total, - fee, - conversionFee, - balance: account.balance - }); - throw new Error( - `Insufficient funds, need ${total + conversionFee} ${ - account.ticker === "BTC" ? "SAT" : account.ticker - }, have ${account.balance}` - ); - } - - await account.decrement( - { balance: total + conversionFee }, - { transaction } - ); - await account.reload({ transaction }); - - let receiverAccount = await db.Account.findOne({ - where: { - "$user.username$": conversionFeeReceiver - }, - include: [ - { - model: db.User, - as: "user" - } - ], - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (conversionFee) { - await receiverAccount.increment( - { balance: conversionFee }, - { transaction } - ); - await receiverAccount.reload({ transaction }); - fee_payment = { - amount: conversionFee, - fee: 0, - memo: "Liquid conversion fee", - account_id: receiverAccount.id, - user_id: receiverAccount.user_id, - rate: store.rates[receiverAccount.user.currency], - currency: receiverAccount.user.currency, - confirmed: true, - received: true, - network: "COINOS" - }; - fee_payment.account = receiverAccount; - ({ id: fee_payment_id } = await db.Payment.create(fee_payment, { - transaction - })); - } - - let payment = { - amount: -amount, - account_id: account.id, - fee, - memo, - user_id: user.id, - rate: store.rates[user.currency], - currency: user.currency, - address, - confirmed: true, - received: false, - network: "liquid", - fee_payment_id - }; - - payment.account = account; - payments.push(payment); - } - } - - signed = await lq.signRawTransactionWithWallet( - await lq.blindRawTransaction(tx.hex) - ); - let txid = Transaction.fromHex(signed.hex).getId(); - - for (let i = 0; i < payments.length; i++) { - let p = payments[i]; - if (p) { - let { account } = p; - p.hash = txid; - p = await db.Payment.create(p, { transaction }); - if (p.user_id === user.id && (account.ticker !== "BTC" || !main)) { - main = p.get({ plain: true }); - main.fee_payment = fee_payment; - main.account = account.get({ plain: true }); - } - } - } - - emit(user.username, "account", main.account); - emit(user.username, "payment", main); - }); - - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); - - let txid = await lq.sendRawTransaction(signed.hex); - l("sent liquid tx", txid, address); - - return main; - } catch (e) { - err("problem sending liquid", e.message); - return e; - } -}; - -export default async (req, res) => { - let { user } = req; - - try { - await requirePin(req); - let result = await sendLiquid({ ...req.body, user }) - if (result instanceof Error) throw result; - res.send(result); - } catch (e) { - console.log(e) - err("problem sending liquid", user.username, e.message); - return res.code(500).send(e); - } -}; diff --git a/routes/lnurl.js b/routes/lnurl.js index 1ad7b814..e9a0b167 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -1,6 +1,5 @@ import app from "$app"; import config from "$config"; -import db from "$db"; import store from "$lib/store"; import { optionalAuth, auth } from "$lib/passport"; import { l, err, warn } from "$lib/logging"; @@ -9,11 +8,9 @@ import ln from "$lib/ln"; import persist from "$lib/persist"; import { emit } from "$lib/sockets"; -import axios from "axios"; import lnurl from "lnurl"; import jwt from "jsonwebtoken"; import qs from "query-string"; -import bolt11 from "bolt11"; import { computeConversionFee, diff --git a/routes/locations.js b/routes/locations.js index 63bfe1a1..1cebc883 100644 --- a/routes/locations.js +++ b/routes/locations.js @@ -1,19 +1,21 @@ import app from "$app"; import redis from "$lib/redis"; -import axios from "axios"; +import got from "got"; -let locations; +let locations = []; app.get("/locations", async (req, res) => { res.send({ locations }); }); let getLocations = async () => { - let { data } = await axios.get( + try { + let r = await got( "https://api.btcmap.org/v2/elements?updated_since=2022-09-19" - ); - locations = data.filter( + ).json(); + locations = r.filter( l => l["osm_json"].tags && l["osm_json"].tags["payment:coinos"] === "yes" ); + } catch(e) {} setTimeout(getLocations, 60000); }; diff --git a/routes/payments.js b/routes/payments.js index 279588d1..7b094ccc 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -1,279 +1,171 @@ 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", + auth, + async ({ uuid, query: { start, end, limit, offset } }, res) => { + if (limit) limit = parseInt(limit); + if (offset) offset = parseInt(offset); -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 } - }); + // 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)); - return payment.get({ plain: true }); - } catch (e) { - console.log(e); + let payments = await redis.get(`${uuid}:payments`); + res.send({ payments, total }); } -}); +); + +// 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); +// } +// }); diff --git a/routes/referrals.js b/routes/referrals.js deleted file mode 100644 index 7193964d..00000000 --- a/routes/referrals.js +++ /dev/null @@ -1,272 +0,0 @@ -import express from "express"; -var router = express.Router(); - -import { v4 } from "uuid"; - -router.get("/", function(req, res, next) { - res.send("list of referrals..."); -}); - -// Grant & Retrieve Referral tokens for existing user - -// usage: GET '/grant?sponsor_id=N&expiry=2021-09-01' -// -// Required: -// sponsor_id (= user.id) -// Changes to DB: -// adds record to referrals table: -// { token: , sponsor_id: , status: "available" } - -/** - * @api {post} /grant Grant Referral code - * @apiName Grant - * @apiGroup Referrals - * - * - * @apiPermission member - * - * @apiParam {sponsor_id} user_id of member generating referral code. - * @apiParam {expiry} optional expiry date for token. - * - * @apiSuccess {String} token referral token - * @apiSuccess {Date} expiry optional expiry - * - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "token": "*********", - * "status": "available", - * "expiry": null - * } - */ -router.post("/grant", auth, async (req, res) => { - const { sponsor_id, expiry } = req.body; - var token = v4(); - - var ref = await knex.table("referrals").insert({ - sponsor_id: sponsor_id, - token: token, - status: "available" - }); - - return res.send({ token: token, status: "available", expiry: expiry }); -}); - -/** - * @api {get} /grant Grant Referral code (variation using get) - * @apiName Grant - * @apiGroup Referrals - * - * @apiPermission member - * - * @apiPermission member - * @apiParam {sponsor_id} user_id of member generating referral code. - * @apiParam {expiry} optional expiry date for token. - * - * @apiSuccess {String} token referral token - * @apiSuccess {Status} status referral status - * @apiSuccess {Date} expiry optional expiry - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "token": "*********", - * "status": "available", - * "expiry": null - * } - */ -router.get("/grant", auth, async (req, res) => { - const { sponsor_id, expiry } = req.query; - var token = v4(); - - var ref = await knex.table("referrals").insert({ - sponsor_id: sponsor_id, - token: token, - status: "available" - }); - - return res.send({ token: token, status: "available", expiry: expiry }); -}); - -/** - * @api {get} /checkTokens/:sponsor_id Retrieve list of referral tokens generated by this user - * - * @apiName checkTokens - * @apiGroup Referrals - * - * - * @apiPermission member - * - * @apiParam {sponsor_id} user_id of member generating referral code. - * @apiParam {status} optional token status. - * - * @apiSuccess tokens array of token objects - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "tokens": [ - * {"token": "*********", "status": "available", "expiry": null} - * ] - * } - */ - -// TO FIX - change sponsor_id so that it is retrieved from current payload (NOT FROM URL) - -router.get("/checkTokens/:sponsor_id", auth, async (req, res) => { - const { sponsor_id } = req.params; - const { status } = req.query; - - var tokens = knex - .select( - "token", - "created_at", - knex.raw("LEFT(created_at,10) as created"), - "username", - "status" - ) - .from("referrals") - .leftJoin("users", "users.id", "referrals.user_id") - .where("sponsor_id", "like", sponsor_id); - - if (status && status !== "all") - tokens = tokens.where("status", "like", status); - const found = await tokens; - - return res.send({ tokens: found }); -}); - -/** - * @api {get} /verify/:user_id Verify token and apply to existing user - * - * @apiName verify - * @apiGroup Referrals - * - * @apiPermission member - * - * @apiParam {user_id} Current user - * @apiParam {token} Token to validate - * - * @apiSuccess verified = true - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "verified": true - * } - * - * @apiDescription referral token is updated from 'available' to 'used' - * @apiDescription referral token is updated with existing user_id - */ -router.get("/verify/:user_id/:token", auth, async (req, res) => { - const { user_id, token } = req.params; - - const found = await knex - .select("status") - .from("referrals") - .where("token", "like", token) - .whereNull("user_id"); - - if (found && found.length) { - if (found[0].status === "available") { - await knex("referrals") - .where("token", "like", token) - .update({ - status: "used", - user_id: user_id, - updated_at: new Date().toISOString().substring(0, 10) - }); - - return res.send({ - verified: true, - sponsor_id: found.sponsor_id, - updated: found.updated_at - }); - } else { - res.code(500).send({ - verified: false, - message: "Referral already " + found[0].status - }); - } - } else { - res.code(500).send("Invalid referral token"); - } -}); - -/** - * @api {post} /joinQueue Join waiting list (track email & phone) - * @apiName joinQueue - * @apiGroup Referrals - * - * @apiParam {email} email address - * @apiParam {phone} phone number - * - * @apiSuccess verified = true - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * "success": true - * "message": 'Added to waiting list' - * } - * - * @apiDescription referral token is updated from 'available' to 'used' - * @apiDescription referral token is updated with existing user_id - */ -router.post("/joinQueue", async (req, res) => { - const { email, phone, user_id } = req.body; - - await knex.table("waiting_list").insert({ - email: email, - phone: phone, - user_id: user_id - }); - - res.send({ - success: true, - message: "Added " + email + " to waiting list " + phone - }); -}); - -router.get("/joinQueue", async (req, res) => { - const { email, phone, user_id } = req.query; - - await knex("waiting_list").insert({ - email: email, - phone: phone, - user_id: user_id - }); - - res.send({ - success: true, - message: "Added " + email + " to waiting list " + phone - }); -}); - -/** - * @api {get} /isReferred/:user_id Check if user is referred - * @apiName isReferred - * @apiGroup Referrals - * - * @apiParam {user_id} user id - * - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * true - * } - * - * @apiDescription returns boolean indicating if user is referred or not - */ -router.get("/isReferred/:user_id", async (req, res) => { - const { user_id } = req.params; - - var referred = await knex("referrals") - .select("referrals.id") - .where("user_id", "like", user_id) - .where("status", "like", "used"); - - if (referred && referred.length) { - res.send({ referred: true }); - } else { - res.send({ referred: false }); - } -}); - -export default router; diff --git a/routes/requests.js b/routes/requests.js index 70ab2b07..0b3832e8 100644 --- a/routes/requests.js +++ b/routes/requests.js @@ -1,8 +1,6 @@ import app from "$app"; -import db from "$db"; import { auth } from "$lib/passport"; import { emit } from "$lib/sockets"; -import { Op, col } from "@sequelize/core"; app.get("/request/:id", auth, async (req, res) => { try { diff --git a/routes/swaps.js b/routes/swaps.js deleted file mode 100644 index 169c2b8f..00000000 --- a/routes/swaps.js +++ /dev/null @@ -1,587 +0,0 @@ -import { emit } from "$lib/sockets"; -import app from "$app"; -import config from "$config"; -import { auth, optionalAuth } from "$lib/passport"; -import store from "$lib/store"; -import axios from "axios"; -import crypto from "crypto"; -import fs from "fs"; -import getAccount from "../lib/account"; -import { Op, col } from "@sequelize/core"; - -const shallow = a => { - let b = {}; - for (x in a) { - if (a[x] instanceof Date || typeof a[x] !== "object") b[x] = a[x]; - } - return b; -}; - -const assets = { - b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23: "bitcoin", - "4eebe36eb0819e6daa5dd3c97733251ff4eb728c810d949365d6dacaad5ef6e8": "tether" -}; - -const timeout = 20000; - -const swap = async (user, { a1, a2, v1, v2 }) => { - if (!parseInt(v1) || v1 < 0 || !parseInt(v2) || v2 < 0) - throw new Error("Invalid amount"); - let rate = v2 / v1; - - const b = await lq.getBalance(); - - Object.keys(b).map(asset => { - assets[asset] = asset; - }); - - assets[config.liquid.btcasset] = "bitcoin"; - assets["ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"] = - "tether"; - assets["a0c358a0f6947864af3a06f3f6a2aeb304df7fd95c922f2f22d7412399ce7691"] = - "adamcoin"; - - if (!assets[a1]) throw new Error("Asset not found on server"); - if (v1 > Math.round(b[assets[a1]] * SATS)) - throw new Error(`Insufficient server funds, ${v1} ${b[assets[a1]]}`); - - await db.transaction(async transaction => { - let a1acc = await getAccount(a1, user, transaction); - let a2acc = await getAccount(a2, user, transaction); - - if (v1 > a1acc.balance) - throw new Error(`Insufficient funds, ${v1} ${a1acc.balance}`); - - l( - `placing order ${v1} ${a1.substr(0, 6)} for ${v2} ${a2.substr(0, 6)}`, - user.username - ); - - await a1acc.decrement({ balance: v1 }, { transaction }); - await a1acc.reload({ transaction }); - - let payment = await db.Payment.create( - { - hash: "Trade Funding", - amount: -v1, - account_id: a1acc.id, - user_id: user.id, - currency: user.currency, - rate: store.rates[user.currency], - confirmed: true, - received: false, - network: "COINOS" - }, - { transaction } - ); - - emit(user.username, "payment", payment.get({ plain: true })); - emit(user.username, "account", a1acc.get({ plain: true })); - - const orders = await db.Order.findAll( - { - where: { - "$acc1.asset$": a2, - "$acc2.asset$": a1, - rate: { [Op.lte]: v1 / v2 }, - accepted: false - }, - order: [ - ["rate", "ASC"], - ["id", "ASC"] - ], - include: [ - { - model: db.User, - as: "user" - }, - { - model: db.Account, - as: "acc1" - }, - { - model: db.Account, - as: "acc2" - } - ] - }, - transaction - ); - - for (let i = 0; i < orders.length; i++) { - if (!v1) break; - let order = orders[i]; - if (order.v2 > v1) { - let rate = order.rate; - await order.decrement( - { - v2: v1 - }, - { transaction } - ); - await order.save({ transaction }); - await order.reload({ transaction }); - order.v1 = Math.round(order.v2 / rate); - await order.save({ transaction }); - - await order.acc1.increment({ balance: v2 }, { transaction }); - await order.acc1.reload({ transaction }); - - emit(order.user.username, "account", order.acc1.get({ plain: true })); - - payment = await db.Payment.create( - { - hash: "Trade Fill", - amount: v1, - account_id: a1acc.id, - user_id: user.id, - currency: user.currency, - rate: store.rates[user.currency], - confirmed: true, - received: true, - network: "COINOS" - }, - { transaction } - ); - - emit(user.username, "payment", payment.get({ plain: true })); - emit(user.username, "account", a1acc.get({ plain: true })); - - if (order.fee) { - const btc = await getAccount( - config.liquid.btcasset, - order.user, - transaction - ); - - payment = await db.Payment.create( - { - hash: "Swap Fee Refund", - amount: order.fee, - account_id: btc.id, - user_id: order.user_id, - currency: order.user.currency, - rate: store.rates[order.user.currency], - confirmed: true, - received: true, - network: "COINOS" - }, - { transaction } - ); - - await btc.increment({ balance: order.fee }, { transaction }); - await btc.reload({ transaction }); - - emit(order.user.username, "account", btc.get({ plain: true })); - } - - emit(order.user.username, "payment", payment.get({ plain: true })); - - order = order.get({ plain: true }); - order.a1 = order.acc1.asset; - order.a2 = order.acc2.asset; - order.rate = order.v2 / order.v1; - - broadcast("order", shallow(order)); - v1 = 0; - } else { - v1 -= order.v2; - v2 = Math.round(v1 * rate); - - order.accepted = true; - order.completedAt = new Date(); - - await order.save({ transaction }); - - let payment = await db.Payment.create( - { - hash: "Trade Fill", - amount: order.v1, - account_id: a2acc.id, - user_id: user.id, - currency: user.currency, - rate: store.rates[user.currency], - confirmed: true, - received: true, - network: "COINOS" - }, - { transaction } - ); - - await a2acc.increment({ balance: order.v1 }, { transaction }); - await a2acc.reload({ transaction }); - - emit(user.username, "payment", payment.get({ plain: true })); - emit(user.username, "account", a2acc.get({ plain: true })); - - payment = await db.Payment.create( - { - hash: "Trade Fill", - amount: order.v2, - account_id: order.a2_id, - user_id: order.user_id, - currency: order.user.currency, - rate: store.rates[order.user.currency], - confirmed: true, - received: true, - network: "COINOS" - }, - { transaction } - ); - - emit(order.user.username, "payment", payment.get({ plain: true })); - await order.acc2.increment({ balance: order.v2 }, { transaction }); - await order.acc2.reload({ transaction }); - - order = order.get({ plain: true }); - order.a1 = order.acc1.asset; - order.a2 = order.acc2.asset; - order.rate = order.v2 / order.v1; - - broadcast("order", shallow(order)); - } - } - - let order; - if (v1) { - order = await db.Order.create( - { - v1, - v2, - user_id: user.id, - a1_id: a1acc.id, - a2_id: a2acc.id - }, - { transaction } - ); - - order = order.get({ plain: true }); - order.rate = v2 / v1; - order.a1 = a1; - order.a2 = a2; - - broadcast("order", order); - } - }); -}; - -const cancel = async (user, id) => { - await db.transaction(async transaction => { - let order = await db.Order.findOne( - { - where: { - id, - user_id: user.id - }, - include: { - model: db.Account, - as: "acc1" - } - }, - - { transaction } - ); - - let { acc1: account, v1, v2 } = order; - await account.increment({ balance: v1 }, { transaction }); - await account.reload({ transaction }); - - let rate = v2 / v1; - let payment = await db.Payment.create( - { - hash: "Trade Cancelled", - amount: v1, - account_id: account.id, - user_id: user.id, - currency: user.currency, - rate: store.rates[user.currency], - confirmed: true, - received: true, - network: "COINOS" - }, - { transaction } - ); - - emit(user.username, "account", account.get({ plain: true })); - emit(user.username, "payment", payment.get({ plain: true })); - - if (order.fee) { - const btc = await getAccount(config.liquid.btcasset, user, transaction); - - payment = await db.Payment.create( - { - hash: "Swap Fee Refund", - amount: order.fee, - account_id: btc.id, - user_id: user.id, - currency: user.currency, - rate: store.rates[user.currency], - confirmed: true, - received: true, - network: "COINOS" - }, - { transaction } - ); - - await btc.increment({ balance: order.fee }, { transaction }); - await btc.reload({ transaction }); - - emit(user.username, "account", btc.get({ plain: true })); - emit(user.username, "payment", payment.get({ plain: true })); - } - - broadcast("removeOrder", id); - await order.destroy({ transaction }); - }); -}; - -app.delete("/order/:id", auth, async (req, res) => { - const { id } = req.params; - const { user } = req; - - await cancel(user, id); - res.send({}); -}); - -app.post("/orders", auth, async (req, res) => { - return res.code(500).send("Trading temporarily disabled"); - const { user } = req; - try { - await swap(user, req.body); - res.send({}); - } catch (e) { - err(req.user.username, e.message); - res.code(500).send(e.message); - } -}); - -app.get("/orders", optionalAuth, async (req, res) => { - try { - let orders = await db.Order.findAll({ - attributes: { - include: [ - [col("acc1.asset"), "a1"], - [col("acc2.asset"), "a2"] - ] - }, - include: [ - { - model: db.Account, - as: "acc1", - attributes: [] - }, - { - model: db.Account, - as: "acc2", - attributes: [] - } - ] - }); - - res.send( - orders.map(order => { - if (order.user_id !== req.user.id) order.user_id = null; - return order; - }) - ); - } catch (e) { - res.code(500).send(e.message); - } -}); - -if (config.maker) { - debug("setup maker account..."); - config.maker.map( - ({ amount, c1, c2, currency, askMultiplier, bidMultiplier }) => { - setInterval(async () => { - if (!rates) return; - - const user = await db.User.findOne({ - where: { - username: "maker" - } - }); - - if (!user) { - debug("maker user not defined in database"); - } else { - debug("maker: " + JSON.stringify(user)); - await db.transaction(async transaction => { - let order = await db.Order.findOne( - { - where: { - user_id: user.id, - "$acc1.asset$": c1, - "$acc2.asset$": c2, - accepted: false - }, - include: [ - { - model: db.Account, - as: "acc1" - }, - { - model: db.Account, - as: "acc2" - } - ] - }, - { transaction } - ); - - let params = { - a1: c1, - a2: c2, - v1: amount * SATS, - v2: Math.round( - amount * - SATS * - (((app.get("ask") * store.rates[currency]) / - store.rates["USD"]) * - askMultiplier) - ) - }; - - if (order) { - let bestBid = await db.Order.findOne( - { - where: { - "$acc1.asset$": c2, - "$acc2.asset$": c1, - accepted: false, - id: { - [Op.ne]: order.id - } - }, - include: [ - { - model: db.Account, - as: "acc1" - }, - { - model: db.Account, - as: "acc2" - } - ], - order: [["rate", "DESC"]], - limit: 1 - }, - { transaction } - ); - if ( - !bestBid || - (params.v2 / params.v1 > bestBid.rate && - order.acc2.balance >= params.v2) - ) { - order.v1 = params.v1; - order.v2 = params.v2; - await order.save(); - order = order.get({ plain: true }); - order.a1 = params.a1; - order.a2 = params.a2; - order.rate = order.v2 / order.v1; - - broadcast("order", shallow(order)); - } - } else { - try { - await swap(user, params); - } catch (e) { - warn("Failed to make ask", e.message); - } - } - - order = await db.Order.findOne( - { - where: { - user_id: user.id, - "$acc1.asset$": c2, - "$acc2.asset$": c1, - accepted: false - }, - include: [ - { - model: db.Account, - as: "acc1" - }, - { - model: db.Account, - as: "acc2" - } - ] - }, - { transaction } - ); - - params = { - a1: c2, - a2: c1, - v1: Math.round( - amount * - SATS * - ((app.get("bid") * store.rates[currency]) / - store.rates["USD"]) * - bidMultiplier - ), - v2: amount * SATS - }; - - if (order) { - let bestAsk = await db.Order.findOne( - { - where: { - "$acc1.asset$": c1, - "$acc2.asset$": c2, - accepted: false, - id: { - [Op.ne]: order.id - } - }, - include: [ - { - model: db.Account, - as: "acc1" - }, - { - model: db.Account, - as: "acc2" - } - ], - order: [["rate", "ASC"]], - limit: 1 - }, - transaction - ); - - if (bestAsk) - if ( - !bestAsk || - (params.v1 / params.v2 < bestAsk.rate && - order.acc1.balance >= params.v1) - ) { - order.v1 = params.v1; - order.v2 = params.v2; - await order.save(); - order = order.get({ plain: true }); - order.a1 = params.a1; - order.a2 = params.a2; - order.rate = order.v2 / order.v1; - - broadcast("order", shallow(order)); - } else { - broadcast("removeOrder", order.id); - await order.destroy({ transaction }); - } - } else { - try { - await swap(user, params); - } catch (e) { - warn("Failed to make bid", e.message); - } - } - }); - } - }, 5000); - } - ); -} diff --git a/routes/tickets.js b/routes/tickets.js deleted file mode 100644 index 7ca56740..00000000 --- a/routes/tickets.js +++ /dev/null @@ -1,22 +0,0 @@ -import app from "$app"; -import redis from "$lib/redis"; -import { auth } from "$lib/passport"; - -app.get("/ticket", async (req, res) => { - res.send({ ticket: await redis.lLen("tickets") }); -}); - -app.get("/tickets", async (req, res) => { - res.send(await redis.lRange("tickets", 0, -1)); -}); - -app.post("/ticket", auth, async (req, res) => { - try { - let { asset } = req.body; - if (!asset || asset.length !== 64) throw new Error("invalid asset"); - - res.send(await redis.rPush("tickets", req.body.asset)); - } catch (e) { - res.code(500).send(e.message); - } -}); diff --git a/routes/users.js b/routes/users.js index 4e6b8a63..4a01cc1e 100644 --- a/routes/users.js +++ b/routes/users.js @@ -7,19 +7,11 @@ import { getUser, nada, uniq, wait } from "$lib/utils"; import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; import { authenticator } from "otplib"; -import Sequelize from "@sequelize/core"; -import { fromBase58, fromPrivateKey } from "bip32"; -import { Mutex } from "async-mutex"; -import bip32 from "bip32"; import whitelist from "$lib/whitelist"; import { l, err, warn } from "$lib/logging"; -import db from "$db"; -import bc from "$lib/bitcoin"; -import lq from "$lib/liquid"; import { emit } from "$lib/sockets"; import register from "$lib/register"; import { requirePin } from "$lib/utils"; -import { coinos, q } from "$lib/nostr"; import got from "got"; import { bech32 } from "bech32"; @@ -30,28 +22,7 @@ const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {}); app.get("/me", auth, async (req, res) => { try { let user = req.user.get({ plain: true }); - let payments = await req.user.getPayments({ - where: { - account_id: user.account_id - }, - order: [["id", "DESC"]], - limit: 12, - include: [ - { - model: db.Account, - as: "account" - }, - { - model: db.Payment, - as: "fee_payment" - } - ] - }); - - user.accounts = await req.user.getAccounts(); - user.payments = payments; user.haspin = !!user.pin; - res.send(pick(user, ...whitelist)); } catch (e) { console.log("problem fetching user", e); @@ -59,53 +30,37 @@ app.get("/me", auth, async (req, res) => { } }); -app.get("/users/:username", async (req, res) => { +app.get("/users/:key", async (req, res) => { try { - let { username } = req.params; - - if (username.startsWith("npub")) { - username = Buffer.from(fromWords(decode(username).words)).toString("hex"); + if (key.startsWith("npub")) { + key = Buffer.from(fromWords(decode(key).words)).toString("hex"); } - let user = await db.User.findOne({ - attributes: [ - "username", - "banner", - "profile", - "address", - "currency", - "pubkey", - "display", - "prompt", - "uuid" - ], - where: { - [Sequelize.Op.or]: [ - Sequelize.where( - Sequelize.fn("lower", Sequelize.col("username")), - username.toLowerCase() - ), - { pubkey: username } - ] - } - }); - - if (user) user = user.get({ plain: true }); - else user = JSON.parse(await redis.get(`user:${username}`)); + user = JSON.parse(await redis.get(`user:${key}`)); - if (!user && username.length === 64) { - let newuser = { - username: username.substr(0, 6), - pubkey: username, + if (!user && key.length === 64) { + user = { + username: key.substr(0, 6), + pubkey: key, anon: true }; - - user = JSON.parse(await redis.get(`user:${newuser.pubkey}`)) || newuser; } if (!user) return res.code(500).send("User not found"); - res.send(user); + let whitelist = [ + "username", + "banner", + "profile", + "address", + "currency", + "pubkey", + "display", + "prompt", + "uuid" + ]; + + res.send(pick(user, ...whitelist)); } catch (e) { console.log(e); res.code(500).send(e.message); @@ -402,8 +357,6 @@ let login = async (req, res) => { }; app.post("/login", login); -app.post("/taboggan", login); -app.post("/doggin", login); app.post("/logout", optionalAuth, async (req, res) => { let { subscription } = req.body; @@ -428,100 +381,6 @@ app.post("/logout", optionalAuth, async (req, res) => { res.send({}); }); -app.post("/account", auth, async (req, res) => { - const { user } = req; - const { - id, - name, - ticker, - precision, - domain, - seed, - pubkey, - privkey, - path, - hide, - index - } = req.body; - - try { - await db.transaction(async transaction => { - let account = await db.Account.findOne({ - where: { id, user_id: user.id }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - let params = { - name, - ticker, - precision, - domain, - seed, - pubkey, - path, - hide, - index, - privkey - }; - - if (!account || (account.pubkey && !pubkey)) delete params.pubkey; - - await db.Account.update(params, { - where: { id, user_id: user.id }, - transaction - }); - - await account.reload({ transaction }); - emit(user.username, "account", account); - res.send({}); - }); - } catch (e) { - err("problem updating account", e.message); - return res.code(500).send("There was a problem updating the account"); - } -}); - -app.post("/shiftAccount", auth, async (req, res) => { - let { user } = req; - let { id } = req.body; - - try { - const account = await db.Account.findOne({ - where: { id } - }); - - if (account.user_id !== user.id) - return res.code(500).send("Failed to open wallet"); - - user.account_id = account.id; - await user.save(); - let payments = await db.Payment.findAll({ - where: { - account_id: id - }, - order: [["id", "DESC"]], - limit: 12, - include: { - model: db.Account, - as: "account" - } - }); - - user = user.get({ plain: true }); - user.payments = payments; - user.account = account.get({ plain: true }); - - emit(user.username, "account", user.account); - emit(user.username, "user", user); - - res.send(user); - } catch (e) { - err("problem switching account", e.message); - return res.code(500).send("There was a problem switching accounts"); - } -}); - app.get("/vapidPublicKey", function(req, res) { res.send(config.vapid.publicKey); }); @@ -548,36 +407,6 @@ app.post("/password", auth, async function(req, res) { res.send(await bcrypt.compare(password, user.password)); }); -app.get("/isInternal", auth, async function(req, res) { - let { user } = req; - let { address } = req.query; - if (!address) throw new Error("Address not provided"); - - let invoice = await db.Invoice.findOne({ - where: { address }, - include: { - attributes: ["username"], - model: db.User, - as: "user" - } - }); - - if (invoice) { - let info; - try { - info = await bc.getAddressInfo(address); - } catch (e) { - info = await lq.getAddressInfo(address); - } - if (info.ismine) { - emit(user.username, "to", invoice.user); - return res.send(true); - } - } - - res.send(false); -}); - app.get("/invoices", auth, async function(req, res) { let invoices = await db.Invoice.findAll({ where: { user_id: req.user.id } @@ -585,22 +414,6 @@ app.get("/invoices", auth, async function(req, res) { res.send(invoices); }); -app.post("/signMessage", auth, async function(req, res) { - let { address, message } = req.body; - - let invoices = await db.Invoice.findAll({ - where: { user_id: req.user.id } - }); - - if (invoices.find(i => i.address === address)) { - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); - return res.send(await bc.signMessage(address, message)); - } - - res.code(500).send("Address not found for user"); -}); - app.post("/otpsecret", auth, async function(req, res) { try { await requirePin(req); @@ -652,105 +465,3 @@ app.post("/checkPassword", auth, async function(req, res) { res.code(500).send("Invalid password"); } }); - -app.get("/:pubkey/follows", async (req, res) => { - try { - let { pubkey } = req.params; - let { tagsonly } = req.query; - - let sub = `${pubkey}:follows`; - q( - sub, - { - limit: 1, - kinds: [3], - authors: [pubkey] - }, - { timeout: 60000, eager: 60000 } - ).catch(nada); - - let tags = JSON.parse(await redis.get(`${pubkey}:follows`)) || []; - if (tagsonly) return res.send(tags); - - let follows = []; - for (let f of tags) { - let [_, pubkey] = f; - - q(`${pubkey}:profile:f1`, { - limit: 1, - kinds: [0], - authors: [pubkey] - }).catch(nada); - - let user = JSON.parse(await redis.get(`user:${pubkey}`)); - - if (!user) - user = { - username: pubkey.substr(0, 6), - pubkey, - anon: true - }; - - follows.push(user); - } - - follows = uniq(follows, e => e.pubkey); - follows.sort((a, b) => a.username.localeCompare(b.username)); - - res.send(follows); - } catch (e) { - console.log(e); - res.code(500).send(e && e.message); - } -}); - -app.get("/:pubkey/followers", async (req, res) => { - try { - let { pubkey } = req.params; - - let pubkeys = [ - ...new Set([ - ...(await got(`${config.nostr}/followers?pubkey=${pubkey}`).json()), - ...(await redis.sMembers(`${pubkey}:followers`)) - ]) - ]; - - let followers = []; - - q( - `${pubkey}:followers`, - { kinds: [3], "#p": [pubkey] }, - { timeout: 60000, eager: 60000 } - ).catch(nada); - - for (let pubkey of pubkeys) { - let user = JSON.parse(await redis.get(`user:${pubkey}`)); - if (!user || !user.updated) { - q(`${pubkey}:profile:f2`, { - limit: 1, - kinds: [0], - authors: [pubkey] - }).catch(nada); - - user = JSON.parse(await redis.get(`user:${pubkey}`)); - } - - if (!user) - user = { - username: pubkey.substr(0, 6), - pubkey, - anon: true - }; - - followers.push(user); - } - - followers = uniq(followers, e => e.pubkey); - followers.sort((a, b) => a.username.localeCompare(b.username)); - - res.send(followers); - } catch (e) { - console.log(e); - res.code(500).send(e && e.message); - } -}); diff --git a/sampleconfig/certbot/.gitkeep b/sampleconfig/certbot/.gitkeep deleted file mode 100644 index 8b137891..00000000 --- a/sampleconfig/certbot/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/sampleconfig/index.js b/sampleconfig/index.js index 3b2f9d02..8683bfa6 100644 --- a/sampleconfig/index.js +++ b/sampleconfig/index.js @@ -1,80 +1,16 @@ -import lnurl from "./lnurl"; - -const db = { - user: "root", - password: "password", - database: "coinos", - options: { - logging: false, - host: "maria", - dialect: "mariadb", - dialectOptions: { multipleStatements: true, timezone: "Etc/GMT+7" } - } -}; - -const btcasset = - "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225"; - export default { - nostr: "http://api:9393", - relay: "ws://nostr:8080", - ipxapi: "4275|PqKjKaxIlzooToria2m4XFLzvHihSpsZMX4hvgxu", - webhooks: { - "http://172.18.0.1:5173/wallet/purchase": "horsey" - }, - postmark: "d64dffd2-84ea-47c7-ba9f-95505332d0ae", - db, - clientVersion: "3b96359d6a6ce68fe4a32f495fc9a6f78af0aa63", - lnurl, - knex: { - client: "mysql2", - connection: db - }, - jwt: Buffer.from( - "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b", - "hex" - ), port: 3119, - taxi: "864234bccdd230ecb6db05ebdf5061c3af42fefb9f186b3326e6454fdd3a9475", + nostr: "ws://nostr:8080", + ipxapi: "4275|PqKjKaxIlzooToria2m4XFLzvHihSpsZMX4hvgxu", + postmark: "f64dffd2-84ea-47c7-ba9f-95e053a2d0ae", + jwt: "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b", bitcoin: { - masterkey: - "tprv8ZgxMBicQKsPdTUoagV41nang9pQUA2DKoVWLmTrWmb2PfsC8pDTHveCMyzUyMguKJCjd5uyHcqg27r7gDzz4TY3MgucpLsSXwCbjn2C3Q1", host: "bitcoin", wallet: "coinos", username: "admin1", password: "123", network: "regtest", port: 18443, - zmqrawblock: "tcp://bitcoin:18506", - zmqrawtx: "tcp://bitcoin:18507" - }, - liquid: { - masterkey: - "tprv8ZgxMBicQKsPfBjZp16yQUmZM3DWh5fb366inBXF3Xk6ANhJYqxXYL4FeQxAW5kjT3ku1A1wmS79c5y6RajKr2TkCADG8A3h4WdggbFMXX1", - blindkey: - "099e599b1d79dbc536c6f8461772d7f2f9583d7b6f3fba70394d60b599c3bd96", - host: "liquid", - username: "admin1", - password: "123", - network: "regtest", - wallet: "coinos", - port: 7040, - zmqrawblock: "tcp://liquid:18606", - zmqrawtx: "tcp://liquid:18607", - btcasset }, - lna: { - clightning: true, - dir: "/app/config/lightning/regtest/lightning-rpc" - /* - lnd: true - socket: "lnd:10009", - // get this with: sudo base64 config/lnd/tls.cert | tr -d '\n' - cert: - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNLVENDQWMrZ0F3SUJBZ0lRVE4wWEhJdTI1eitKaUpmVUxSOTROVEFLQmdncWhrak9QUVFEQWpBNE1SOHcKSFFZRFZRUUtFeFpzYm1RZ1lYVjBiMmRsYm1WeVlYUmxaQ0JqWlhKME1SVXdFd1lEVlFRREV3d3haREkyWVRkagpObUZqTVRZd0hoY05Nakl3TlRFMU1qSTFOVFEyV2hjTk1qTXdOekV3TWpJMU5UUTJXakE0TVI4d0hRWURWUVFLCkV4WnNibVFnWVhWMGIyZGxibVZ5WVhSbFpDQmpaWEowTVJVd0V3WURWUVFERXd3eFpESTJZVGRqTm1Gak1UWXcKV1RBVEJnY3Foa2pPUFFJQkJnZ3Foa2pPUFFNQkJ3TkNBQVFjdzRtR0lNbEpkTWsvT2pEWkJ6U21HTm5kY3pQZgphSGlCQytqd0F5RVBnOUhqY3o2MlhYSXdxcTFiV2xmOGtuck5lamg2cm5zQTgrYkNOWTBkNFlxRW80RzZNSUczCk1BNEdBMVVkRHdFQi93UUVBd0lDcERBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEUKQlRBREFRSC9NQjBHQTFVZERnUVdCQlNZTmx4NTBXU2VIN0VTSTZ2eVJCL2JNMG01QmpCZ0JnTlZIUkVFV1RCWApnZ3d4WkRJMllUZGpObUZqTVRhQ0NXeHZZMkZzYUc5emRJSURiRzVrZ2dSMWJtbDRnZ3AxYm1sNGNHRmphMlYwCmdnZGlkV1pqYjI1dWh3Ui9BQUFCaHhBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaHdTc0VnQUhNQW9HQ0NxR1NNNDkKQkFNQ0EwZ0FNRVVDSUU5M3hBWEFxQzBuejdqRWVXWVN3czB2dlZWZmcvSEFNYnNLSzVXMFFNMCtBaUVBME5EVgpOV0VVdU9Vdk4ydTZ0UjBpRFh3dXhPMmJtTDltVEZwejhHc3RUKzA9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", - // get this with: sudo base64 config/lnd/data/chain/bitcoin/regtest/admin.macaroon | tr -d '\n' - macaroon: - "AgEDbG5kAvgBAwoQFdsmSt7tRRKg8kFR+avH0BIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaIQoIbWFjYXJvb24SCGdlbmVyYXRlEgRyZWFkEgV3cml0ZRoWCgdtZXNzYWdlEgRyZWFkEgV3cml0ZRoXCghvZmZjaGFpbhIEcmVhZBIFd3JpdGUaFgoHb25jaGFpbhIEcmVhZBIFd3JpdGUaFAoFcGVlcnMSBHJlYWQSBXdyaXRlGhgKBnNpZ25lchIIZ2VuZXJhdGUSBHJlYWQAAAYgR93/t3TVABMMqjYOdcYG/B70ZczhSoLXVT7NlocedG0=" - */ - } + lightning: "/app/config/lightning/regtest/lightning-rpc" }; diff --git a/sampleconfig/liquid/elements.conf b/sampleconfig/liquid/elements.conf deleted file mode 100644 index 3aa177d0..00000000 --- a/sampleconfig/liquid/elements.conf +++ /dev/null @@ -1,18 +0,0 @@ -chain=liquidregtest -listen=1 -txindex=1 - -[liquidregtest] -rpcuser=admin1 -rpcpassword=123 -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 -rpcwallet=coinos - -validatepegin=0 -initialfreecoins=2100000000000000 -wallet=coinos -wallet=external - -zmqpubrawblock=tcp://0.0.0.0:18606 -zmqpubrawtx=tcp://0.0.0.0:18607 diff --git a/sampleconfig/lnd/lnd.conf b/sampleconfig/lnd/lnd.conf deleted file mode 100644 index a979284a..00000000 --- a/sampleconfig/lnd/lnd.conf +++ /dev/null @@ -1,25 +0,0 @@ -externalip=lnd:9735 -alias=coinos -color=#FF3200 -tlsextradomain=lnd -listen=0.0.0.0:9735 -rpclisten=0.0.0.0:10009 -restlisten=0.0.0.0:8080 -accept-amp=1 -accept-keysend=1 - -# uncomment this after lnd wallet is initialized -# wallet-unlock-password-file=/root/.lnd/pwd - -[Bitcoin] -bitcoin.active=1 -bitcoin.node=bitcoind -bitcoin.regtest=1 - -[Bitcoind] -bitcoind.rpchost=bitcoin -bitcoind.dir=/root/.bitcoin -bitcoind.rpcuser=admin1 -bitcoind.rpcpass=123 -bitcoind.zmqpubrawblock=tcp://bitcoin:18506 -bitcoind.zmqpubrawtx=tcp://bitcoin:18507 diff --git a/sampleconfig/lnd/pwd b/sampleconfig/lnd/pwd deleted file mode 100644 index f3097ab1..00000000 --- a/sampleconfig/lnd/pwd +++ /dev/null @@ -1 +0,0 @@ -password diff --git a/sampleconfig/lnurl.js b/sampleconfig/lnurl.js deleted file mode 100644 index b101e23c..00000000 --- a/sampleconfig/lnurl.js +++ /dev/null @@ -1,36 +0,0 @@ -import fs from "fs"; - -export default { - host: "app", - port: 3118, - protocol: "http", - url: "https://staging.coinos.io", - endpoint: "/lnurl", - listen: true, - auth: { - apiKeys: [ - { - id: "50bdeb634d", - key: "ac9bdd15a983072311c74c13877d122f16a27a8f40cb4dfa5014eece4da5cda2" - } - ] - }, - lightning: { - backend: "c-lightning", - config: { - unixSockPath: "/app/config/lightning/regtest/lightning-rpc" - } - }, - store: { - backend: "knex", - config: { - client: "mysql", - connection: { - host: "maria", - user: "root", - password: "password", - database: "coinos" - } - } - } -}; diff --git a/sampleconfig/nginx/default.conf.template b/sampleconfig/nginx/default.conf.template deleted file mode 100644 index 453f368f..00000000 --- a/sampleconfig/nginx/default.conf.template +++ /dev/null @@ -1,38 +0,0 @@ -server { - listen 80; - server_name .${NGINX_HOST}; - server_tokens off; - - location @rewrites { - rewrite ^(.+)$ /index.html last; - } - - location / { - root /coinos; - index index.html; - try_files $uri $uri/ /index.html; - } - - location /api/ { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-Proto https; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Host $remote_addr; - rewrite ^ $request_uri?; - rewrite ^/api/(.*) /$1 break; - return 400; - proxy_pass http://app:3119$uri$is_args; - } - - location /ws { - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_http_version 1.1; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $host; - rewrite ^/ws/(.*) /$1 break; - - proxy_pass http://app:3119; - } -} diff --git a/sampledata/conversions.json b/sampledata/conversions.json deleted file mode 100644 index 2c63c085..00000000 --- a/sampledata/conversions.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/sampledata/emails.json b/sampledata/emails.json deleted file mode 100644 index 5fbac846..00000000 --- a/sampledata/emails.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "emails": [ - ] -} diff --git a/sampledata/logins.json b/sampledata/logins.json deleted file mode 100644 index 0967ef42..00000000 --- a/sampledata/logins.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/sampledata/payments.json b/sampledata/payments.json deleted file mode 100644 index 2c63c085..00000000 --- a/sampledata/payments.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/sampledata/recipients.json b/sampledata/recipients.json deleted file mode 100644 index 2c63c085..00000000 --- a/sampledata/recipients.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/sampledata/withdrawals.json b/sampledata/withdrawals.json deleted file mode 100644 index 0967ef42..00000000 --- a/sampledata/withdrawals.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/scripts/apiParse.js b/scripts/apiParse.js deleted file mode 100644 index ec2a33c1..00000000 --- a/scripts/apiParse.js +++ /dev/null @@ -1,46 +0,0 @@ -export const addUserSearch = function(input, table) { - var { search, starts_with, contains } = input; - - if (!table) { - table = "users"; - } - // if (since) cmd = cmd.havingRaw('invoices + payments + deposits + orders > ?', [0]) - - if (search) { - if (starts_with) { - search = search + "%"; - } else if (contains) { - search = "%" + search + "%"; - } - - var condition = - "(" + - table + - ".email like '" + - search + - "' OR " + - table + - ".username like '" + - search + - "')"; - - return condition; - } else { - return ""; - } -}; - -export const addTimeSearch = function(input, field) { - if (!field) { - field = "updatedAt"; - } - - var { since } = input; - if (since) { - condition = field + " >= '" + since + "'"; - - return condition; - } else { - return ""; - } -}; diff --git a/scripts/check.js b/scripts/check.js deleted file mode 100644 index e86940da..00000000 --- a/scripts/check.js +++ /dev/null @@ -1,24 +0,0 @@ -let config = require("./config"); -let fs = require("fs"); - -let mailgun = require("mailgun-js")({ - apiKey: config.mailgun, - domain: config.domain -}); - -let data = { - ...config.mail, - subject: "User Documents", - text: "Documents uploaded" -}; - -fs.readdir("./uploads", (err, files) => { - files.forEach(file => { - console.log(file); - data.text = file; - fs.rename(file, "../verified" + file); - mailgun.messages().send(data, function(error, body) { - console.log(body); - }); - }); -}); diff --git a/scripts/purge-except-git.sh b/scripts/purge-except-git.sh deleted file mode 100755 index 066b082f..00000000 --- a/scripts/purge-except-git.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/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) diff --git a/secret b/secret deleted file mode 100644 index e69de29b..00000000 -- cgit v1.2.3