2020-07-21 14:36:50 +03:00
|
|
|
import * as Environment from "~/node_common/environment";
|
2020-07-22 08:53:29 +03:00
|
|
|
import * as Strings from "~/common/strings";
|
2020-08-01 03:17:07 +03:00
|
|
|
import * as Constants from "~/node_common/constants";
|
2020-09-22 03:36:45 +03:00
|
|
|
import * as Social from "~/node_common/social";
|
2020-06-17 21:05:13 +03:00
|
|
|
|
2020-10-27 07:41:42 +03:00
|
|
|
import crypto from "crypto";
|
2020-07-22 08:53:29 +03:00
|
|
|
import JWT from "jsonwebtoken";
|
2020-08-11 08:15:39 +03:00
|
|
|
import BCrypt from "bcrypt";
|
2020-07-20 09:06:36 +03:00
|
|
|
|
2020-10-27 07:41:42 +03:00
|
|
|
const ENCRYPTION_ALGORITHM = "aes-256-ctr";
|
|
|
|
const ENCRYPTION_IV = crypto.randomBytes(16);
|
|
|
|
|
2020-09-23 01:05:58 +03:00
|
|
|
import { Buckets, PrivateKey, Pow, Client, ThreadID } from "@textile/hub";
|
2020-07-20 09:06:36 +03:00
|
|
|
|
|
|
|
const BUCKET_NAME = "data";
|
|
|
|
|
|
|
|
const TEXTILE_KEY_INFO = {
|
2020-07-21 14:36:50 +03:00
|
|
|
key: Environment.TEXTILE_HUB_KEY,
|
|
|
|
secret: Environment.TEXTILE_HUB_SECRET,
|
|
|
|
};
|
|
|
|
|
2020-08-20 08:57:46 +03:00
|
|
|
export const checkTextile = async () => {
|
|
|
|
try {
|
2020-08-25 20:46:41 +03:00
|
|
|
const response = await fetch("https://slate.textile.io/health", {
|
2020-08-20 08:57:46 +03:00
|
|
|
headers: {
|
|
|
|
Accept: "application/json",
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
if (response.status === 204) {
|
|
|
|
return true;
|
|
|
|
}
|
2020-09-23 03:34:17 +03:00
|
|
|
|
|
|
|
Social.sendTextileSlackMessage({
|
|
|
|
file: "/node_common/utilities.js",
|
|
|
|
user: { username: "UNDEFINED" },
|
|
|
|
message: "https://slate.textile.io/health is down",
|
|
|
|
code: "N/A",
|
|
|
|
functionName: `checkTextile`,
|
|
|
|
});
|
2020-08-20 08:57:46 +03:00
|
|
|
} catch (e) {
|
2020-09-23 03:34:17 +03:00
|
|
|
Social.sendTextileSlackMessage({
|
|
|
|
file: "/node_common/utilities.js",
|
|
|
|
user: { username: "UNDEFINED" },
|
|
|
|
message: e.message,
|
|
|
|
code: e.code,
|
|
|
|
functionName: `checkTextile`,
|
|
|
|
});
|
2020-08-20 08:57:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2020-07-22 13:51:40 +03:00
|
|
|
export const getIdFromCookie = (req) => {
|
2020-09-03 03:35:41 +03:00
|
|
|
let id = null;
|
|
|
|
if (Strings.isEmpty(req.headers.cookie)) {
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
const token = req.headers.cookie.replace(
|
|
|
|
/(?:(?:^|.*;\s*)WEB_SERVICE_SESSION_KEY\s*\=\s*([^;]*).*$)|^.*$/,
|
|
|
|
"$1"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!Strings.isEmpty(token)) {
|
|
|
|
try {
|
|
|
|
const decoded = JWT.verify(token, Environment.JWT_SECRET);
|
|
|
|
id = decoded.id;
|
|
|
|
} catch (e) {
|
2020-09-23 04:24:00 +03:00
|
|
|
console.log(e.message);
|
2020-07-22 08:53:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-22 13:51:40 +03:00
|
|
|
return id;
|
2020-07-22 08:53:29 +03:00
|
|
|
};
|
|
|
|
|
2020-10-27 07:41:42 +03:00
|
|
|
export const encryptWithSecret = async (text, secret) => {
|
|
|
|
const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, secret, ENCRYPTION_IV);
|
|
|
|
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
|
|
|
|
|
|
|
return {
|
|
|
|
iv: ENCRYPTION_IV.toString("hex"),
|
|
|
|
hex: encrypted.toString("hex"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-08-11 08:19:02 +03:00
|
|
|
export const encryptPassword = async (text, salt) => {
|
2020-08-11 08:15:39 +03:00
|
|
|
if (!text) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
let hash = text;
|
|
|
|
for (let i = 0; i < Environment.LOCAL_PASSWORD_ROUNDS_MANUAL; i++) {
|
|
|
|
hash = await BCrypt.hash(hash, salt);
|
|
|
|
}
|
|
|
|
hash = await BCrypt.hash(hash, Environment.LOCAL_PASSWORD_SECRET);
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
};
|
|
|
|
|
2020-07-21 14:36:50 +03:00
|
|
|
export const parseAuthHeader = (value) => {
|
|
|
|
if (typeof value !== "string") {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
var matches = value.match(/(\S+)\s+(\S+)/);
|
|
|
|
return matches && { scheme: matches[1], value: matches[2] };
|
2020-07-20 09:06:36 +03:00
|
|
|
};
|
|
|
|
|
2020-09-09 20:56:35 +03:00
|
|
|
// NOTE(jim): Requires @textile/hub
|
2020-09-23 12:46:59 +03:00
|
|
|
export const getPowergateAPIFromUserToken = async ({ user }) => {
|
|
|
|
const token = user.data.tokens.api;
|
2020-09-09 20:56:35 +03:00
|
|
|
const identity = await PrivateKey.fromString(token);
|
2020-09-18 00:14:34 +03:00
|
|
|
const power = await Pow.withKeyInfo(TEXTILE_KEY_INFO);
|
2020-09-09 20:56:35 +03:00
|
|
|
await power.getToken(identity);
|
2020-09-22 03:36:45 +03:00
|
|
|
|
|
|
|
// TODO(jim): Put this call into a file for all Textile related calls.
|
|
|
|
let info = {};
|
|
|
|
try {
|
|
|
|
const powerInfoResponse = await power.info();
|
|
|
|
info = powerInfoResponse.info;
|
|
|
|
} catch (e) {
|
|
|
|
Social.sendTextileSlackMessage({
|
|
|
|
file: "/node_common/utilities.js",
|
|
|
|
user,
|
|
|
|
message: e.message,
|
|
|
|
code: e.code,
|
|
|
|
functionName: `power.info`,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(jim): Put this call into a file for all Textile related calls.
|
|
|
|
let health = {};
|
|
|
|
try {
|
|
|
|
health = await power.health();
|
|
|
|
} catch (e) {
|
|
|
|
Social.sendTextileSlackMessage({
|
|
|
|
file: "/node_common/utilities.js",
|
|
|
|
user,
|
|
|
|
message: e.message,
|
|
|
|
code: e.code,
|
|
|
|
functionName: `power.health`,
|
|
|
|
});
|
|
|
|
}
|
2020-09-09 20:56:35 +03:00
|
|
|
|
|
|
|
return {
|
|
|
|
power,
|
|
|
|
powerHealth: health,
|
|
|
|
powerInfo: info,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-09-27 05:08:07 +03:00
|
|
|
export const setupWithThread = async ({ buckets }) => {
|
|
|
|
const client = new Client(buckets.context);
|
|
|
|
|
|
|
|
try {
|
|
|
|
const res = await client.getThread("buckets");
|
|
|
|
|
|
|
|
buckets.withThread(res.id.toString());
|
|
|
|
} catch (error) {
|
|
|
|
if (error.message !== "Thread not found") {
|
|
|
|
throw new Error(error.message);
|
|
|
|
}
|
|
|
|
|
|
|
|
const newId = ThreadID.fromRandom();
|
|
|
|
await client.newDB(newId, "buckets");
|
|
|
|
const threadID = newId.toString();
|
|
|
|
|
|
|
|
buckets.withThread(threadID);
|
|
|
|
}
|
|
|
|
|
|
|
|
return buckets;
|
|
|
|
};
|
|
|
|
|
2020-10-05 00:30:28 +03:00
|
|
|
export const addExistingCIDToData = async ({ buckets, key, path, cid }) => {
|
|
|
|
try {
|
|
|
|
await buckets.setPath(key, path || "/", cid);
|
|
|
|
return true;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-20 09:06:36 +03:00
|
|
|
// NOTE(jim): Requires @textile/hub
|
2020-09-16 11:40:03 +03:00
|
|
|
export const getBucketAPIFromUserToken = async ({ user, bucketName, encrypted = false }) => {
|
2020-09-23 12:46:59 +03:00
|
|
|
const token = user.data.tokens.api;
|
|
|
|
const name = Strings.isEmpty(bucketName) ? BUCKET_NAME : bucketName;
|
2020-08-19 23:44:53 +03:00
|
|
|
const identity = await PrivateKey.fromString(token);
|
2020-10-06 22:07:43 +03:00
|
|
|
let buckets = await Buckets.withKeyInfo(TEXTILE_KEY_INFO);
|
2020-09-23 12:46:59 +03:00
|
|
|
|
2020-07-20 09:06:36 +03:00
|
|
|
await buckets.getToken(identity);
|
2020-09-22 03:36:45 +03:00
|
|
|
|
2020-09-23 12:46:59 +03:00
|
|
|
let root = null;
|
2020-09-27 05:08:07 +03:00
|
|
|
|
2020-09-23 12:46:59 +03:00
|
|
|
console.log(`[ buckets ] getOrCreate init ${name}`);
|
2020-09-25 09:42:21 +03:00
|
|
|
|
2020-09-27 05:08:07 +03:00
|
|
|
// NOTE(jim): captures `withThread` cases.
|
2020-09-22 03:36:45 +03:00
|
|
|
try {
|
2020-09-27 05:08:07 +03:00
|
|
|
buckets = await setupWithThread({ buckets });
|
|
|
|
} catch (e) {
|
|
|
|
console.log(`[ textile ] warning: ${e.message}`);
|
|
|
|
}
|
2020-09-23 12:46:59 +03:00
|
|
|
|
2020-09-27 05:08:07 +03:00
|
|
|
console.log(`[ buckets ] getOrCreate thread found for ${name}`);
|
2020-09-23 00:31:19 +03:00
|
|
|
|
2020-09-27 05:08:07 +03:00
|
|
|
// NOTE(jim): captures finding your bucket and or creating a new one.
|
|
|
|
try {
|
2020-09-23 03:34:17 +03:00
|
|
|
const roots = await buckets.list();
|
2020-09-23 12:46:59 +03:00
|
|
|
root = roots.find((bucket) => bucket.name === name);
|
2020-09-23 00:31:19 +03:00
|
|
|
if (!root) {
|
2020-09-23 12:46:59 +03:00
|
|
|
console.log(`[ buckets ] creating new bucket ${name}`);
|
2020-09-25 09:42:21 +03:00
|
|
|
|
|
|
|
if (encrypted) {
|
|
|
|
console.log("[ buckets ] this bucket will be encrypted");
|
|
|
|
}
|
|
|
|
|
|
|
|
const created = await buckets.create(name, encrypted);
|
2020-09-23 03:34:17 +03:00
|
|
|
root = created.root;
|
2020-09-23 00:31:19 +03:00
|
|
|
}
|
2020-09-22 03:36:45 +03:00
|
|
|
} catch (e) {
|
|
|
|
Social.sendTextileSlackMessage({
|
|
|
|
file: "/node_common/utilities.js",
|
|
|
|
user,
|
|
|
|
message: e.message,
|
|
|
|
code: e.code,
|
|
|
|
functionName: `buckets.getOrCreate`,
|
|
|
|
});
|
|
|
|
|
2020-09-23 00:39:57 +03:00
|
|
|
return { buckets: null, bucketKey: null, bucketRoot: null };
|
2020-09-22 03:36:45 +03:00
|
|
|
}
|
2020-09-23 03:34:17 +03:00
|
|
|
|
2020-09-23 12:46:59 +03:00
|
|
|
console.log(`[ buckets ] getOrCreate success for ${name}`);
|
2020-09-25 09:42:21 +03:00
|
|
|
|
2020-09-23 00:39:57 +03:00
|
|
|
return {
|
|
|
|
buckets,
|
|
|
|
bucketKey: root.key,
|
|
|
|
bucketRoot: root,
|
2020-09-23 12:46:59 +03:00
|
|
|
bucketName: name,
|
2020-09-23 00:39:57 +03:00
|
|
|
};
|
2020-07-20 09:06:36 +03:00
|
|
|
};
|
|
|
|
|
2020-06-17 21:05:13 +03:00
|
|
|
export const getFileName = (s) => {
|
|
|
|
let target = s;
|
2020-07-09 06:19:08 +03:00
|
|
|
if (target.endsWith("/")) {
|
2020-06-17 21:05:13 +03:00
|
|
|
target = target.substring(0, target.length - 1);
|
|
|
|
}
|
|
|
|
|
2020-07-09 06:19:08 +03:00
|
|
|
return target.substr(target.lastIndexOf("/") + 1);
|
2020-06-17 21:05:13 +03:00
|
|
|
};
|
|
|
|
|
2020-07-21 08:45:15 +03:00
|
|
|
export const createFolder = ({ id, name }) => {
|
2020-06-17 21:05:13 +03:00
|
|
|
return {
|
2020-07-09 06:19:08 +03:00
|
|
|
decorator: "FOLDER",
|
2020-06-17 21:05:13 +03:00
|
|
|
id,
|
|
|
|
folderId: id,
|
2020-07-09 06:19:08 +03:00
|
|
|
icon: "FOLDER",
|
2020-07-21 08:45:15 +03:00
|
|
|
name: name ? name : getFileName(id),
|
|
|
|
pageTitle: `Exploring ${getFileName(id)}`,
|
2020-06-17 21:05:13 +03:00
|
|
|
date: null,
|
|
|
|
size: null,
|
|
|
|
children: [],
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export const updateStateData = async (state, newState) => {
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
...newState,
|
|
|
|
};
|
|
|
|
};
|