mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-22 21:45:56 +03:00
postgres: user accounts, migration scripts, api route rewrite, stopping point
This commit is contained in:
parent
ee9e8fd27d
commit
b6a8c880cd
@ -16,7 +16,10 @@ Working on Slate requires an internet connection because we are using a hosted P
|
|||||||
|
|
||||||
### .env
|
### .env
|
||||||
|
|
||||||
You will need to create a file called `.env`. Never commit your credentials to the repository. **You don't need this file if you only work on the design system.**
|
- We use a `dotenv` file to manage sensitive values and secrets.
|
||||||
|
- You must create this file to work on the application.
|
||||||
|
- You don't need to create a `.env` file if you're only working on the design system.
|
||||||
|
- There will be no local data in the short term.
|
||||||
|
|
||||||
```
|
```
|
||||||
POSTGRES_ADMIN_PASSWORD=XXX
|
POSTGRES_ADMIN_PASSWORD=XXX
|
||||||
|
@ -8,25 +8,8 @@ const REQUEST_HEADERS = {
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
};
|
};
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV !== "production";
|
const dev = process.env.NODE_ENV !== "www";
|
||||||
|
const SERVER_PATH = dev ? "http://localhost:1337" : "https://slate.host";
|
||||||
const SERVER_PATH = dev
|
|
||||||
? "http://localhost:1337"
|
|
||||||
: "https://filecoin.onrender.com";
|
|
||||||
|
|
||||||
export const rehydrateViewer = async () => {
|
|
||||||
const options = {
|
|
||||||
method: "POST",
|
|
||||||
headers: REQUEST_HEADERS,
|
|
||||||
credentials: "include",
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await fetch(`${SERVER_PATH}/_/viewer`, options);
|
|
||||||
const json = await response.json();
|
|
||||||
|
|
||||||
return json;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setDefaultConfig = async (data) => {
|
export const setDefaultConfig = async (data) => {
|
||||||
const options = {
|
const options = {
|
||||||
@ -85,3 +68,58 @@ export const sendWalletAddressFilecoin = async (data) => {
|
|||||||
|
|
||||||
return json;
|
return json;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOTE(jim):
|
||||||
|
// New WWW Requests.
|
||||||
|
export const hydrateAuthenticatedUser = async (data) => {
|
||||||
|
const options = {
|
||||||
|
method: "POST",
|
||||||
|
headers: REQUEST_HEADERS,
|
||||||
|
credentials: "include",
|
||||||
|
body: JSON.stringify({ data }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(`${SERVER_PATH}/api/hydrate`, options);
|
||||||
|
const json = await response.json();
|
||||||
|
|
||||||
|
return json;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteUser = async (data) => {
|
||||||
|
const options = {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: REQUEST_HEADERS,
|
||||||
|
credentials: "include",
|
||||||
|
body: JSON.stringify({ data }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(`${SERVER_PATH}/api/users/delete`, options);
|
||||||
|
const json = await response.json();
|
||||||
|
return json;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createUser = async (data) => {
|
||||||
|
const options = {
|
||||||
|
method: "POST",
|
||||||
|
headers: REQUEST_HEADERS,
|
||||||
|
credentials: "include",
|
||||||
|
body: JSON.stringify({ data }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(`${SERVER_PATH}/api/users/create`, options);
|
||||||
|
const json = await response.json();
|
||||||
|
return json;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const health = async (data) => {
|
||||||
|
const options = {
|
||||||
|
method: "POST",
|
||||||
|
headers: REQUEST_HEADERS,
|
||||||
|
credentials: "include",
|
||||||
|
body: JSON.stringify({ data: { success: true } }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(`${SERVER_PATH}/api/_`, options);
|
||||||
|
const json = await response.json();
|
||||||
|
return json;
|
||||||
|
};
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
if (process.env.NODE_ENV !== "www") {
|
|
||||||
console.log("[ prototype ] loading dotenv");
|
|
||||||
require("dotenv").config();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
development: {
|
development: {
|
||||||
client: "pg",
|
client: "pg",
|
||||||
|
6
node_common/data/index.js
Normal file
6
node_common/data/index.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import createUser from "~/node_common/data/methods/create-user";
|
||||||
|
import updateUserById from "~/node_common/data/methods/update-user-by-id";
|
||||||
|
import deleteUserByUsername from "~/node_common/data/methods/delete-user-by-username";
|
||||||
|
import getUserByUsername from "~/node_common/data/methods/get-user-by-username";
|
||||||
|
|
||||||
|
export { createUser, updateUserById, deleteUserByUsername, getUserByUsername };
|
27
node_common/data/methods/create-user.js
Normal file
27
node_common/data/methods/create-user.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { runQuery } from "~/node_common/data/utilities";
|
||||||
|
|
||||||
|
export default async ({ email, password, username, salt, data = {} }) => {
|
||||||
|
return await runQuery({
|
||||||
|
label: "CREATE_USER",
|
||||||
|
queryFn: async (DB) => {
|
||||||
|
const query = await DB.insert({
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
salt,
|
||||||
|
data,
|
||||||
|
username,
|
||||||
|
})
|
||||||
|
.into("users")
|
||||||
|
.returning("*");
|
||||||
|
|
||||||
|
const index = query ? query.pop() : null;
|
||||||
|
return index;
|
||||||
|
},
|
||||||
|
errorFn: async (e) => {
|
||||||
|
return {
|
||||||
|
error: "CREATE_USER",
|
||||||
|
source: e,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
20
node_common/data/methods/delete-user-by-username.js
Normal file
20
node_common/data/methods/delete-user-by-username.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { runQuery } from "~/node_common/data/utilities";
|
||||||
|
|
||||||
|
export default async ({ username }) => {
|
||||||
|
return await runQuery({
|
||||||
|
label: "DELETE_USER_BY_USERNAME",
|
||||||
|
queryFn: async (DB) => {
|
||||||
|
const data = await DB.from("users")
|
||||||
|
.where({ username })
|
||||||
|
.del();
|
||||||
|
|
||||||
|
return 1 === data;
|
||||||
|
},
|
||||||
|
errorFn: async (e) => {
|
||||||
|
return {
|
||||||
|
error: "DELETE_USER_BY_USERNAME",
|
||||||
|
source: e,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
29
node_common/data/methods/get-user-by-username.js
Normal file
29
node_common/data/methods/get-user-by-username.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { runQuery } from "~/node_common/data/utilities";
|
||||||
|
|
||||||
|
export default async ({ username }) => {
|
||||||
|
return await runQuery({
|
||||||
|
label: "GET_USER_BY_USERNAME",
|
||||||
|
queryFn: async (DB) => {
|
||||||
|
const query = await DB.select("*")
|
||||||
|
.from("users")
|
||||||
|
.where({ username })
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (!query || query.error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.id) {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
errorFn: async (e) => {
|
||||||
|
return {
|
||||||
|
error: "GET_USER_BY_USERNAME",
|
||||||
|
source: e,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
26
node_common/data/methods/update-user-by-id.js
Normal file
26
node_common/data/methods/update-user-by-id.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { runQuery } from "~/node_common/data/utilities";
|
||||||
|
|
||||||
|
export default async ({ id, data }) => {
|
||||||
|
return await runQuery({
|
||||||
|
label: "UPDATE_USER_BY_ID",
|
||||||
|
queryFn: async (DB) => {
|
||||||
|
const data = await DB.from("users")
|
||||||
|
.where("id", o.id)
|
||||||
|
.update({
|
||||||
|
data: {
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.returning("*");
|
||||||
|
|
||||||
|
const index = data ? data.pop() : null;
|
||||||
|
return index;
|
||||||
|
},
|
||||||
|
errorFn: async (e) => {
|
||||||
|
return {
|
||||||
|
error: "UPDATE_USER",
|
||||||
|
source: e,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
13
node_common/data/utilities.js
Normal file
13
node_common/data/utilities.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import DB from "~/node_common/database";
|
||||||
|
|
||||||
|
export const runQuery = async ({ queryFn, errorFn, label }) => {
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await queryFn(DB);
|
||||||
|
} catch (e) {
|
||||||
|
response = errorFn(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[ database-query ]", { query: label });
|
||||||
|
return response;
|
||||||
|
};
|
@ -6,6 +6,6 @@ import configs from "~/knexfile";
|
|||||||
import knex from "knex";
|
import knex from "knex";
|
||||||
|
|
||||||
const envConfig = configs["development"];
|
const envConfig = configs["development"];
|
||||||
const db = knex(envConfig);
|
const Database = knex(envConfig);
|
||||||
|
|
||||||
module.exports = db;
|
export default Database;
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
|
export const init = (middleware) => {
|
||||||
|
return (req, res) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
middleware(req, res, (result) => {
|
||||||
|
if (result instanceof Error) {
|
||||||
|
return reject(result);
|
||||||
|
}
|
||||||
|
return resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const CORS = async (req, res, next) => {
|
export const CORS = async (req, res, next) => {
|
||||||
res.header("Access-Control-Allow-Origin", "*");
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
res.header(
|
res.header(
|
||||||
"Access-Control-Allow-Methods",
|
"Access-Control-Allow-Methods",
|
||||||
"GET, POST, PATCH, PUT, DELETE, OPTIONS"
|
"GET, POST, PATCH, PUT, DELETE, OPTIONS"
|
||||||
);
|
);
|
||||||
|
|
||||||
res.header(
|
res.header(
|
||||||
"Access-Control-Allow-Headers",
|
"Access-Control-Allow-Headers",
|
||||||
"Origin, Accept, Content-Type, Authorization"
|
"Origin, Accept, Content-Type, Authorization"
|
9
node_common/powergate.js
Normal file
9
node_common/powergate.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import * as Constants from "~/node_common/constants";
|
||||||
|
|
||||||
|
import { createPow } from "@textile/powergate-client";
|
||||||
|
|
||||||
|
// NOTE(jim):
|
||||||
|
// https://github.com/textileio/js-powergate-client
|
||||||
|
const Powergate = createPow({ host: Constants.POWERGATE_HOST });
|
||||||
|
|
||||||
|
export default Powergate;
|
@ -1,34 +1,5 @@
|
|||||||
import * as Constants from "./constants";
|
import * as Constants from "./constants";
|
||||||
|
|
||||||
import FS from "fs-extra";
|
|
||||||
|
|
||||||
export const resetFileSystem = async () => {
|
|
||||||
console.log("[ prototype ] deleting old token and library data ");
|
|
||||||
if (FS.existsSync(`./.data`)) {
|
|
||||||
FS.removeSync("./.data", { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[ prototype ] deleting old avatar data ");
|
|
||||||
if (FS.existsSync(Constants.AVATAR_STORAGE_URL)) {
|
|
||||||
FS.removeSync(Constants.AVATAR_STORAGE_URL, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[ prototype ] deleting old file data ");
|
|
||||||
if (FS.existsSync(Constants.FILE_STORAGE_URL)) {
|
|
||||||
FS.removeSync(Constants.FILE_STORAGE_URL, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[ prototype ] creating new avatar folder ");
|
|
||||||
FS.mkdirSync(Constants.AVATAR_STORAGE_URL, { recursive: true });
|
|
||||||
FS.writeFileSync(`${Constants.AVATAR_STORAGE_URL}.gitkeep`, "");
|
|
||||||
|
|
||||||
console.log("[ prototype ] creating new local file folder ");
|
|
||||||
FS.mkdirSync(Constants.FILE_STORAGE_URL, { recursive: true });
|
|
||||||
FS.writeFileSync(`${Constants.FILE_STORAGE_URL}.gitkeep`, "");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOTE(jim): Data that does not require a Powergate token.
|
// NOTE(jim): Data that does not require a Powergate token.
|
||||||
export const refresh = async ({ PG }) => {
|
export const refresh = async ({ PG }) => {
|
||||||
const Health = await PG.health.check();
|
const Health = await PG.health.check();
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"node": ">=11 <12"
|
"node": ">=11 <12"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node . --unhandled-rejections=strict",
|
"dev": "NODE_TLS_REJECT_UNAUTHORIZED=0 node . --unhandled-rejections=strict",
|
||||||
"start": "NODE_ENV=www node . --unhandled-rejections=strict",
|
"start": "NODE_ENV=www node . --unhandled-rejections=strict",
|
||||||
"build-delete": "rm -rf .next && rm -rf dist/mac",
|
"build-delete": "rm -rf .next && rm -rf dist/mac",
|
||||||
"build": "NODE_ENV=www next build",
|
"build": "NODE_ENV=www next build",
|
||||||
@ -44,7 +44,9 @@
|
|||||||
"@emotion/server": "11.0.0-next.12",
|
"@emotion/server": "11.0.0-next.12",
|
||||||
"@textile/hub": "^0.3.4",
|
"@textile/hub": "^0.3.4",
|
||||||
"@textile/powergate-client": "0.1.0-beta.14",
|
"@textile/powergate-client": "0.1.0-beta.14",
|
||||||
|
"@textile/threads-core": "^0.1.32",
|
||||||
"babel-plugin-module-resolver": "^4.0.0",
|
"babel-plugin-module-resolver": "^4.0.0",
|
||||||
|
"bcrypt": "^5.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"chart.js": "^2.9.3",
|
"chart.js": "^2.9.3",
|
||||||
"chartkick": "^3.2.0",
|
"chartkick": "^3.2.0",
|
||||||
@ -54,6 +56,7 @@
|
|||||||
"formidable": "^1.2.2",
|
"formidable": "^1.2.2",
|
||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
"knex": "^0.20.10",
|
"knex": "^0.20.10",
|
||||||
"moment": "^2.27.0",
|
"moment": "^2.27.0",
|
||||||
"next": "^9.4.4",
|
"next": "^9.4.4",
|
||||||
@ -65,8 +68,7 @@
|
|||||||
"react-tippy": "^1.3.4",
|
"react-tippy": "^1.3.4",
|
||||||
"regenerator-runtime": "^0.13.5",
|
"regenerator-runtime": "^0.13.5",
|
||||||
"three": "^0.108.0",
|
"three": "^0.108.0",
|
||||||
"uuid": "^8.0.0",
|
"uuid": "^8.0.0"
|
||||||
"ws": "^7.3.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-transform-runtime": "^7.10.4",
|
"@babel/plugin-transform-runtime": "^7.10.4",
|
||||||
|
13
pages/api/_.js
Normal file
13
pages/api/_.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as MW from "~/node_common/middleware";
|
||||||
|
|
||||||
|
import DB from "~/node_common/database";
|
||||||
|
|
||||||
|
const initCORS = MW.init(MW.CORS);
|
||||||
|
|
||||||
|
export default (req, res) => {
|
||||||
|
initCORS(req, res);
|
||||||
|
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ decorator: "SERVER_HEALTH_CHECK", data: req.body.data });
|
||||||
|
};
|
71
pages/api/hydrate.js
Normal file
71
pages/api/hydrate.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import * as MW from "~/node_common/middleware";
|
||||||
|
import * as Utilities from "~/node_common/utilities";
|
||||||
|
import * as Constants from "~/node_common/constants";
|
||||||
|
import * as Data from "~/node_common/data";
|
||||||
|
|
||||||
|
import { Buckets, UserAuth } from "@textile/hub";
|
||||||
|
import { Libp2pCryptoIdentity } from "@textile/threads-core";
|
||||||
|
|
||||||
|
import PG from "~/node_common/powergate";
|
||||||
|
|
||||||
|
const initCORS = MW.init(MW.CORS);
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
initCORS(req, res);
|
||||||
|
|
||||||
|
const user = await Data.getUserByUsername({
|
||||||
|
username: req.body.data.username,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res.status(200).json({ decorator: "SERVER_HYDRATE", error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.error) {
|
||||||
|
return res.status(200).json({ decorator: "SERVER_HYDRATE", error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const identity = await Libp2pCryptoIdentity.fromString(user.data.tokens.api);
|
||||||
|
|
||||||
|
const b = Buckets.withUserAuth(identity);
|
||||||
|
// TODO(jim): Bug on the server:
|
||||||
|
// const buckets = await b.list();
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
peersList: null,
|
||||||
|
messageList: null,
|
||||||
|
status: null,
|
||||||
|
addrsList: null,
|
||||||
|
info: null,
|
||||||
|
state: null,
|
||||||
|
local: {
|
||||||
|
photo: null,
|
||||||
|
name: `node`,
|
||||||
|
settings_deals_auto_approve: false,
|
||||||
|
},
|
||||||
|
library: [
|
||||||
|
{
|
||||||
|
...Utilities.createFolder({ id: Constants.FILE_STORAGE_URL }),
|
||||||
|
file: "Files",
|
||||||
|
name: "Files",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buckets: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
PG.setToken(user.data.tokens.pg);
|
||||||
|
|
||||||
|
const updates = await Utilities.refresh({ PG });
|
||||||
|
const updatesWithToken = await Utilities.refreshWithToken({
|
||||||
|
PG,
|
||||||
|
});
|
||||||
|
|
||||||
|
data = await Utilities.updateStateData(data, {
|
||||||
|
...updates,
|
||||||
|
...updatesWithToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.send({ decorator: "SERVER_HYDRATE", success: true, data });
|
||||||
|
};
|
72
pages/api/users/create.js
Normal file
72
pages/api/users/create.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import * as MW from "~/node_common/middleware";
|
||||||
|
import * as Data from "~/node_common/data";
|
||||||
|
import * as Strings from "~/common/strings";
|
||||||
|
|
||||||
|
import PG from "~/node_common/powergate";
|
||||||
|
import JWT from "jsonwebtoken";
|
||||||
|
import BCrypt from "bcrypt";
|
||||||
|
|
||||||
|
import { Libp2pCryptoIdentity } from "@textile/threads-core";
|
||||||
|
|
||||||
|
const initCORS = MW.init(MW.CORS);
|
||||||
|
const SECRET = `$2b$13$${process.env.LOCAL_PASSWORD_SECRET}`;
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
initCORS(req, res);
|
||||||
|
|
||||||
|
if (Strings.isEmpty(req.body.data.email)) {
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.send({ error: "An e-mail address was not provided." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Strings.isEmpty(req.body.data.password)) {
|
||||||
|
return res.status(500).send({ error: "A password was not provided." });
|
||||||
|
}
|
||||||
|
|
||||||
|
const salt = await BCrypt.genSalt(13);
|
||||||
|
console.log({ salt });
|
||||||
|
const hash = await BCrypt.hash(req.body.data.password, salt);
|
||||||
|
console.log({ hash });
|
||||||
|
const double = await BCrypt.hash(hash, salt);
|
||||||
|
console.log({
|
||||||
|
double,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(SECRET);
|
||||||
|
|
||||||
|
const triple = await BCrypt.hash(double, SECRET);
|
||||||
|
console.log({ triple });
|
||||||
|
|
||||||
|
const FFS = await PG.ffs.create();
|
||||||
|
const pg = FFS.token ? FFS.token : null;
|
||||||
|
|
||||||
|
// API
|
||||||
|
const identity = await Libp2pCryptoIdentity.fromRandom();
|
||||||
|
const api = identity.toString();
|
||||||
|
|
||||||
|
const user = await Data.createUser({
|
||||||
|
email: req.body.data.email,
|
||||||
|
password: triple,
|
||||||
|
salt,
|
||||||
|
username: req.body.data.username,
|
||||||
|
data: { tokens: { pg, api } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ decorator: "SERVER_USER_CREATE", error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.error) {
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ decorator: "SERVER_USER_CREATE", error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
decorator: "SERVER_USER_CREATE",
|
||||||
|
user: { username: user.username },
|
||||||
|
});
|
||||||
|
};
|
20
pages/api/users/delete.js
Normal file
20
pages/api/users/delete.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import * as MW from "~/node_common/middleware";
|
||||||
|
import * as Data from "~/node_common/data";
|
||||||
|
|
||||||
|
const initCORS = MW.init(MW.CORS);
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
initCORS(req, res);
|
||||||
|
|
||||||
|
const deleted = await Data.deleteUserByUsername({
|
||||||
|
username: req.body.data.username,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!deleted) {
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({ decorator: "SERVER_USER_DELETE", error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({ decorator: "SERVER_USER_DELETE", deleted });
|
||||||
|
};
|
11
pages/api/users/update.js
Normal file
11
pages/api/users/update.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import * as MW from "~/node_common/middleware";
|
||||||
|
|
||||||
|
import DB from "~/node_common/database";
|
||||||
|
|
||||||
|
const initCORS = MW.init(MW.CORS);
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
initCORS(req, res);
|
||||||
|
|
||||||
|
return res.status(200).json({ decorator: "SERVER_USER_UPDATE" });
|
||||||
|
};
|
@ -65,10 +65,8 @@ const getCurrentNavigationStateById = (navigation, targetId) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getServerSideProps = async (context) => {
|
export const getServerSideProps = async (context) => {
|
||||||
const data = await Actions.rehydrateViewer();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { ...context.query, ...data.data },
|
props: { ...context.query },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,24 +77,47 @@ export default class ApplicationPage extends React.Component {
|
|||||||
history: [{ id: 1, scrollTop: 0 }],
|
history: [{ id: 1, scrollTop: 0 }],
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
data: null,
|
data: null,
|
||||||
selected: State.getSelectedState(this.props),
|
selected: null,
|
||||||
viewer: State.getInitialState(this.props),
|
viewer: null,
|
||||||
sidebar: null,
|
sidebar: null,
|
||||||
file: null,
|
file: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
async componentDidMount() {
|
||||||
this._socket = new WebSocket(`ws://localhost:${this.props.wsPort}`);
|
if (this.props.production) {
|
||||||
this._socket.onmessage = (m) => {
|
console.log("Disabled application in production setting for now.");
|
||||||
console.log(m);
|
return null;
|
||||||
if (m.type === "message") {
|
}
|
||||||
const parsed = JSON.parse(m.data);
|
|
||||||
|
|
||||||
if (parsed.action === "UPDATE_VIEWER") {
|
let response = await Actions.deleteUser({
|
||||||
this.rehydrate({ data: parsed.data });
|
username: "test",
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
console.log(response);
|
||||||
|
|
||||||
|
response = await Actions.createUser({
|
||||||
|
email: "test@test.com",
|
||||||
|
password: "test",
|
||||||
|
username: "test",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
console.log("Could not create a new user");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
response = await Actions.hydrateAuthenticatedUser({
|
||||||
|
username: "test",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
viewer: State.getInitialState(response.data),
|
||||||
|
selected: State.getSelectedState(response.data),
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener("dragenter", this._handleDragEnter);
|
window.addEventListener("dragenter", this._handleDragEnter);
|
||||||
window.addEventListener("dragleave", this._handleDragLeave);
|
window.addEventListener("dragleave", this._handleDragLeave);
|
||||||
@ -327,6 +348,11 @@ export default class ApplicationPage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
// TODO(jim): Render Sign In Screen.
|
||||||
|
if (!this.state.viewer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const navigation = NavigationData.generate(this.state.viewer.library);
|
const navigation = NavigationData.generate(this.state.viewer.library);
|
||||||
const next = this.state.history[this.state.currentIndex];
|
const next = this.state.history[this.state.currentIndex];
|
||||||
const current = getCurrentNavigationStateById(navigation, next.id);
|
const current = getCurrentNavigationStateById(navigation, next.id);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as Constants from "~/common/constants";
|
import * as Constants from "~/common/constants";
|
||||||
|
import * as Actions from "~/common/actions";
|
||||||
|
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
|
|
||||||
@ -18,6 +19,10 @@ const STYLES_HEADING = css`
|
|||||||
font-size: 2.88rem;
|
font-size: 2.88rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: ${Constants.system.black};
|
color: ${Constants.system.black};
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_PARAGRAPH = css`
|
const STYLES_PARAGRAPH = css`
|
||||||
@ -25,6 +30,10 @@ const STYLES_PARAGRAPH = css`
|
|||||||
font-size: 2.88rem;
|
font-size: 2.88rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: ${Constants.system.pitchBlack};
|
color: ${Constants.system.pitchBlack};
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const getServerSideProps = async (context) => {
|
export const getServerSideProps = async (context) => {
|
||||||
@ -34,6 +43,11 @@ export const getServerSideProps = async (context) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default class IndexPage extends React.Component {
|
export default class IndexPage extends React.Component {
|
||||||
|
async componentDidMount() {
|
||||||
|
const response = await Actions.health();
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const title = `Slate`;
|
const title = `Slate`;
|
||||||
const description =
|
const description =
|
||||||
@ -54,9 +68,7 @@ export default class IndexPage extends React.Component {
|
|||||||
<a href="https://filecoin.io/">Filecoin</a>.
|
<a href="https://filecoin.io/">Filecoin</a>.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{!this.props.hide ? (
|
<a href="/application">Test Application (Local Only)</a>
|
||||||
<a href="/application">Test Application</a>
|
|
||||||
) : null}
|
|
||||||
<br />
|
<br />
|
||||||
<a href="/system">View Design System</a>
|
<a href="/system">View Design System</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
if (process.env.NODE_ENV !== "www") {
|
||||||
|
console.log("[ slate ] loading dotenv");
|
||||||
|
require("dotenv").config();
|
||||||
|
}
|
||||||
|
|
||||||
require("@babel/register")({
|
require("@babel/register")({
|
||||||
presets: ["@babel/preset-env"],
|
presets: ["@babel/preset-env"],
|
||||||
ignore: ["node_modules", ".next"],
|
ignore: ["node_modules", ".next"],
|
||||||
|
426
server.js
426
server.js
@ -1,455 +1,49 @@
|
|||||||
if (process.env.NODE_ENV !== "www") {
|
if (process.env.NODE_ENV !== "www") {
|
||||||
console.log("[ prototype ] loading dotenv");
|
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
}
|
}
|
||||||
|
|
||||||
import * as Middleware from "./common/middleware";
|
|
||||||
import * as Strings from "./common/strings";
|
import * as Strings from "./common/strings";
|
||||||
|
import * as Middleware from "./node_common/middleware";
|
||||||
import * as Utilities from "./node_common/utilities";
|
import * as Utilities from "./node_common/utilities";
|
||||||
import * as Constants from "./node_common/constants";
|
import * as Constants from "./node_common/constants";
|
||||||
import * as Database from "./node_common/database";
|
|
||||||
|
|
||||||
import { createPow, ffs } from "@textile/powergate-client";
|
|
||||||
|
|
||||||
// NOTE(jim):
|
|
||||||
// https://github.com/textileio/js-powergate-client
|
|
||||||
const PowerGate = createPow({ host: Constants.POWERGATE_HOST });
|
|
||||||
|
|
||||||
import { v4 as uuid } from "uuid";
|
|
||||||
// TODO(jim):
|
|
||||||
// CUT THIS DURING THE POSTGRES MOVE.
|
|
||||||
import FS from "fs-extra";
|
|
||||||
import WebSocketServer from "ws";
|
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import formidable from "formidable";
|
|
||||||
import next from "next";
|
import next from "next";
|
||||||
import bodyParser from "body-parser";
|
|
||||||
import compression from "compression";
|
import compression from "compression";
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
// TODO(jim):
|
|
||||||
// CUT THIS DURING THE POSTGRES MOVE.
|
|
||||||
let client = null;
|
|
||||||
let state = null;
|
|
||||||
|
|
||||||
const production =
|
const production =
|
||||||
process.env.NODE_ENV === "production" || process.env.NODE_ENV === "www";
|
process.env.NODE_ENV === "production" || process.env.NODE_ENV === "www";
|
||||||
const productionWeb = process.env.NODE_ENV === "www";
|
const productionWeb = process.env.NODE_ENV === "www";
|
||||||
const port = process.env.PORT || 1337;
|
const port = process.env.PORT || 1337;
|
||||||
const wsPort = process.env.WS_PORT || 2448;
|
|
||||||
const resetData = process.env.npm_config_reset_data;
|
|
||||||
const app = next({ dev: !production, dir: __dirname, quiet: false });
|
const app = next({ dev: !production, dir: __dirname, quiet: false });
|
||||||
const nextRequestHandler = app.getRequestHandler();
|
const handler = app.getRequestHandler();
|
||||||
|
|
||||||
const setIntervalViewerUpdatesUnsafe = async () => {
|
|
||||||
if (client) {
|
|
||||||
try {
|
|
||||||
console.log("[ prototype ] polling: new viewer state.");
|
|
||||||
state = await Utilities.emitState({
|
|
||||||
state,
|
|
||||||
client,
|
|
||||||
PG: PowerGate,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("[ prototype ] polling: new library state.");
|
|
||||||
state = await Utilities.refreshLibrary({
|
|
||||||
state,
|
|
||||||
PG: PowerGate,
|
|
||||||
FFS: ffs,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(setIntervalViewerUpdatesUnsafe, Constants.POLLING_RATE);
|
|
||||||
};
|
|
||||||
|
|
||||||
app.prepare().then(async () => {
|
app.prepare().then(async () => {
|
||||||
console.log("[ prototype ] initializing ");
|
|
||||||
|
|
||||||
// TODO(jim):
|
|
||||||
// CUT THIS DURING THE POSTGRES MOVE.
|
|
||||||
state = {
|
|
||||||
production,
|
|
||||||
port,
|
|
||||||
wsPort,
|
|
||||||
token: null,
|
|
||||||
library: null,
|
|
||||||
status: null,
|
|
||||||
messageList: null,
|
|
||||||
peersList: null,
|
|
||||||
addrsList: null,
|
|
||||||
info: null,
|
|
||||||
local: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// TODO(jim):
|
|
||||||
// CUT THIS DURING THE POSTGRES MOVE.
|
|
||||||
// NOTE(daniel): Wipe all of the local data when --reset-data flag is added to npm run dev.
|
|
||||||
if (resetData) {
|
|
||||||
await Utilities.resetFileSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
const updates = await Utilities.refresh({ PG: PowerGate });
|
|
||||||
state = await Utilities.updateStateData(state, updates);
|
|
||||||
console.log("[ prototype ] updated without token");
|
|
||||||
|
|
||||||
// NOTE(jim): This is a configuration folder with all of the client tokens.
|
|
||||||
// TODO(jim): Unnecessary if we use a local and remote postgres.
|
|
||||||
const dirnameData = path.join(__dirname, "/.data");
|
|
||||||
if (!FS.existsSync(dirnameData)) {
|
|
||||||
FS.mkdirSync(dirnameData, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(jim): This will create a token for authentication with powergate.
|
|
||||||
// TODO(jim): Roll this up into Postgres instead.
|
|
||||||
const dirnamePowergate = path.join(__dirname, "/.data/powergate-token");
|
|
||||||
if (!FS.existsSync(dirnamePowergate)) {
|
|
||||||
const FFS = await PowerGate.ffs.create();
|
|
||||||
state.token = FFS.token ? FFS.token : null;
|
|
||||||
|
|
||||||
// NOTE(jim): Write a new token file.
|
|
||||||
if (state.token) {
|
|
||||||
FS.writeFileSync(dirnamePowergate, state.token);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.token = FS.readFileSync(dirnamePowergate, "utf8");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.token) {
|
|
||||||
console.log("[ prototype ] powergate token:", state.token);
|
|
||||||
PowerGate.setToken(state.token);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenUpdates = await Utilities.refreshWithToken({
|
|
||||||
PG: PowerGate,
|
|
||||||
});
|
|
||||||
state = await Utilities.updateStateData(state, tokenUpdates);
|
|
||||||
console.log("[ prototype ] updated with token");
|
|
||||||
|
|
||||||
// NOTE(jim): Local library retrieval or creation
|
|
||||||
// TODO(jim): Needs to support nested folders in the future.
|
|
||||||
// TODO(jim): May consider a move to buckets.
|
|
||||||
const dirnameLibrary = path.join(__dirname, "/.data/library.json");
|
|
||||||
if (!FS.existsSync(dirnameLibrary)) {
|
|
||||||
const librarySchema = {
|
|
||||||
library: [
|
|
||||||
{
|
|
||||||
...Utilities.createFolder({ id: Constants.FILE_STORAGE_URL }),
|
|
||||||
file: "Files",
|
|
||||||
name: "Files",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
FS.writeFileSync(dirnameLibrary, JSON.stringify(librarySchema));
|
|
||||||
state.library = librarySchema.library;
|
|
||||||
} else {
|
|
||||||
const parsedLibrary = FS.readFileSync(dirnameLibrary, "utf8");
|
|
||||||
state.library = JSON.parse(parsedLibrary).library;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(jim): Local settings retrieval or creation
|
|
||||||
// TODO(jim): Move this to postgres later.
|
|
||||||
const dirnameLocalSettings = path.join(
|
|
||||||
__dirname,
|
|
||||||
"/.data/local-settings.json"
|
|
||||||
);
|
|
||||||
if (!FS.existsSync(dirnameLocalSettings)) {
|
|
||||||
const localSettingsSchema = {
|
|
||||||
local: {
|
|
||||||
photo: null,
|
|
||||||
name: `node-${uuid()}`,
|
|
||||||
settings_deals_auto_approve: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
FS.writeFileSync(
|
|
||||||
dirnameLocalSettings,
|
|
||||||
JSON.stringify(localSettingsSchema)
|
|
||||||
);
|
|
||||||
state.local = localSettingsSchema.local;
|
|
||||||
} else {
|
|
||||||
const parsedLocal = FS.readFileSync(dirnameLocalSettings, "utf8");
|
|
||||||
state.local = JSON.parse(parsedLocal).local;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
console.log('[ prototype ] "/" -- WILL REDIRECT TO /SYSTEM ');
|
|
||||||
console.log(
|
|
||||||
"[ prototype ] SLATE WILL NOT RUN LOCALLY UNTIL YOU HAVE "
|
|
||||||
);
|
|
||||||
console.log("[ prototype ] PROPERLY CONFIGURED POWERGATE AND ");
|
|
||||||
console.log(
|
|
||||||
"[ prototype ] CONNECTED TO THE FILECOIN NETWORK (DEVNET/TESTNET) "
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const server = express();
|
const server = express();
|
||||||
// TODO(jim): Temporarily disable web sockets for web production
|
|
||||||
// since we have no web version of Slate yet.
|
|
||||||
if (!productionWeb) {
|
|
||||||
const WSS = new WebSocketServer.Server({ port: wsPort });
|
|
||||||
|
|
||||||
WSS.on("connection", (s) => {
|
|
||||||
// TODO(jim): Suppport more than one client.
|
|
||||||
client = s;
|
|
||||||
|
|
||||||
s.on("close", function() {
|
|
||||||
s.send(JSON.stringify({ action: null, data: "closed" }));
|
|
||||||
});
|
|
||||||
|
|
||||||
s.send(JSON.stringify({ action: null, data: "connected" }));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (productionWeb) {
|
if (productionWeb) {
|
||||||
server.use(compression());
|
server.use(compression());
|
||||||
}
|
}
|
||||||
|
|
||||||
server.use(Middleware.CORS);
|
|
||||||
server.use("/public", express.static("public"));
|
server.use("/public", express.static("public"));
|
||||||
server.use(bodyParser.json());
|
|
||||||
server.use(
|
|
||||||
bodyParser.urlencoded({
|
|
||||||
extended: false,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
server.post("/_/viewer", async (req, res) => {
|
|
||||||
let data = state;
|
|
||||||
|
|
||||||
if (!productionWeb) {
|
|
||||||
const updates = await Utilities.refresh({ PG: PowerGate });
|
|
||||||
const updatesWithToken = await Utilities.refreshWithToken({
|
|
||||||
PG: PowerGate,
|
|
||||||
});
|
|
||||||
data = await Utilities.updateStateData(data, {
|
|
||||||
...updates,
|
|
||||||
...updatesWithToken,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).send({ success: true, data });
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/_/deals/storage", async (req, res) => {
|
|
||||||
if (Strings.isEmpty(req.body.src)) {
|
|
||||||
return res.status(500).send({ success: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
const localPath = `.${req.body.src}`;
|
|
||||||
const buffer = FS.readFileSync(localPath);
|
|
||||||
const { cid } = await PowerGate.ffs.addToHot(buffer);
|
|
||||||
const { jobId } = await PowerGate.ffs.pushConfig(cid);
|
|
||||||
|
|
||||||
// TODO(jim): Refactor this so we repeat this less often.
|
|
||||||
let write = false;
|
|
||||||
for (let i = 0; i < state.library.length; i++) {
|
|
||||||
for (let j = 0; j < state.library[i].children.length; j++) {
|
|
||||||
if (localPath === state.library[i].children[j].id) {
|
|
||||||
state.library[i].children[j].job_id = jobId;
|
|
||||||
state.library[i].children[j].cid = cid;
|
|
||||||
state.library[i].children[j].storage_status = 1;
|
|
||||||
write = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(jim): Writes the updated deal state.
|
|
||||||
if (write) {
|
|
||||||
const dirnameLibrary = path.join(__dirname, "/.data/library.json");
|
|
||||||
FS.writeFileSync(
|
|
||||||
dirnameLibrary,
|
|
||||||
JSON.stringify({ library: state.library })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = await Utilities.emitState({ state, client, PG: PowerGate });
|
|
||||||
return res.status(200).send({ success: true, cid, jobId });
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/_/storage/:file", async (req, res) => {
|
|
||||||
const form = formidable({
|
|
||||||
multiples: true,
|
|
||||||
uploadDir: Constants.FILE_STORAGE_URL,
|
|
||||||
});
|
|
||||||
|
|
||||||
form.parse(req, async (error, fields, files) => {
|
|
||||||
if (error) {
|
|
||||||
return res.status(500).send({ error });
|
|
||||||
} else {
|
|
||||||
// TODO(jim): Need to support other file types.
|
|
||||||
if (!files.image) {
|
|
||||||
console.error("[ prototype ] File type unspported", files);
|
|
||||||
return res
|
|
||||||
.status(500)
|
|
||||||
.send({ error: "File type unsupported", files });
|
|
||||||
}
|
|
||||||
|
|
||||||
const newPath = form.uploadDir + req.params.file;
|
|
||||||
FS.rename(files.image.path, newPath, function(err) {});
|
|
||||||
|
|
||||||
const localFile = Utilities.createFile({
|
|
||||||
id: newPath,
|
|
||||||
data: files.image,
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO(jim): Messy, refactor.
|
|
||||||
let pushed = false;
|
|
||||||
for (let i = 0; i < state.library.length; i++) {
|
|
||||||
if (!pushed) {
|
|
||||||
state.library[i].children.push(localFile);
|
|
||||||
pushed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(jim): Writes the added file.
|
|
||||||
if (pushed) {
|
|
||||||
const dirnameLibrary = path.join(__dirname, "/.data/library.json");
|
|
||||||
FS.writeFileSync(
|
|
||||||
dirnameLibrary,
|
|
||||||
JSON.stringify({ library: state.library })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = await Utilities.emitState({
|
|
||||||
state,
|
|
||||||
client,
|
|
||||||
PG: PowerGate,
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send({ success: true, file: localFile });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/_/upload/avatar", async (req, res) => {
|
|
||||||
const form = formidable({
|
|
||||||
multiples: true,
|
|
||||||
uploadDir: Constants.AVATAR_STORAGE_URL,
|
|
||||||
});
|
|
||||||
|
|
||||||
form.parse(req, async (error, fields, files) => {
|
|
||||||
if (error) {
|
|
||||||
return res.status(500).send({ error });
|
|
||||||
} else {
|
|
||||||
const newName = `avatar-${uuid()}.png`;
|
|
||||||
const newPath = form.uploadDir + newName;
|
|
||||||
FS.rename(files.image.path, newPath, function(err) {});
|
|
||||||
|
|
||||||
// NOTE(jim): updates avatar photo.
|
|
||||||
state.local.photo = __dirname + `/static/system/${newName}`;
|
|
||||||
const dirnameLocalSettings = path.join(
|
|
||||||
__dirname,
|
|
||||||
"/.data/local-settings.json"
|
|
||||||
);
|
|
||||||
FS.writeFileSync(
|
|
||||||
dirnameLocalSettings,
|
|
||||||
JSON.stringify({ local: { ...state.local } })
|
|
||||||
);
|
|
||||||
|
|
||||||
state = await Utilities.emitState({
|
|
||||||
state,
|
|
||||||
client,
|
|
||||||
PG: PowerGate,
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send({ success: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/_/settings", async (req, res) => {
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = await PowerGate.ffs.setDefaultConfig(req.body.config);
|
|
||||||
} catch (e) {
|
|
||||||
return res.status(500).send({ error: e.message });
|
|
||||||
}
|
|
||||||
|
|
||||||
state = await Utilities.emitState({ state, client, PG: PowerGate });
|
|
||||||
return res.status(200).send({ success: true, data });
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/_/local-settings", async (req, res) => {
|
|
||||||
state.local = { ...state.local, ...req.body.local };
|
|
||||||
const dirnameLocalSettings = path.join(
|
|
||||||
__dirname,
|
|
||||||
"/.data/local-settings.json"
|
|
||||||
);
|
|
||||||
FS.writeFileSync(
|
|
||||||
dirnameLocalSettings,
|
|
||||||
JSON.stringify({ local: { ...state.local } })
|
|
||||||
);
|
|
||||||
state = await Utilities.emitState({ state, client, PG: PowerGate });
|
|
||||||
return res.status(200).send({ success: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/_/wallet/create", async (req, res) => {
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = await PowerGate.ffs.newAddr(
|
|
||||||
req.body.name,
|
|
||||||
req.body.type,
|
|
||||||
req.body.makeDefault
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
return res.status(500).send({ error: e.message });
|
|
||||||
}
|
|
||||||
|
|
||||||
state = await Utilities.emitState({ state, client, PG: PowerGate });
|
|
||||||
return res.status(200).send({ success: true, data });
|
|
||||||
});
|
|
||||||
|
|
||||||
server.post("/_/wallet/send", async (req, res) => {
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = await PowerGate.ffs.sendFil(
|
|
||||||
req.body.source,
|
|
||||||
req.body.target,
|
|
||||||
req.body.amount
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
return res.status(500).send({ error: e.message });
|
|
||||||
}
|
|
||||||
|
|
||||||
state = await Utilities.emitState({ state, client, PG: PowerGate });
|
|
||||||
return res
|
|
||||||
.status(200)
|
|
||||||
.send({ success: true, data: { ...data, ...req.body } });
|
|
||||||
});
|
|
||||||
|
|
||||||
server.get("/application", async (req, res) => {
|
server.get("/application", async (req, res) => {
|
||||||
return app.render(req, res, "/application", {
|
return app.render(req, res, "/application", {
|
||||||
wsPort,
|
wsPort: null,
|
||||||
|
production: productionWeb,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/", async (req, res) => {
|
server.get("/", async (req, res) => {
|
||||||
return app.render(req, res, "/", { hide: productionWeb });
|
return app.render(req, res, "/");
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("*", async (req, res) => {
|
server.all("*", async (req, res) => {
|
||||||
return nextRequestHandler(req, res, req.url);
|
return handler(req, res, req.url);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(port, async (err) => {
|
server.listen(port, async (e) => {
|
||||||
if (err) {
|
if (e) throw e;
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[ prototype ] client: http://localhost:${port}`);
|
console.log(`[ slate ] client: http://localhost:${port}`);
|
||||||
console.log(`[ prototype ] constants:`, Constants);
|
|
||||||
console.log(
|
|
||||||
`[ prototype ] .env postgres hostname: ${process.env.POSTGRES_HOSTNAME}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!productionWeb) {
|
|
||||||
await setIntervalViewerUpdatesUnsafe();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user