aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/ApiManagers/DeleteManager.ts85
-rw-r--r--src/server/ApiManagers/GeneralGoogleManager.ts19
-rw-r--r--src/server/ApiManagers/GooglePhotosManager.ts2
-rw-r--r--src/server/ApiManagers/SessionManager.ts2
-rw-r--r--src/server/ApiManagers/UploadManager.ts2
-rw-r--r--src/server/ApiManagers/UserManager.ts2
-rw-r--r--src/server/DashSession/DashSessionAgent.ts2
-rw-r--r--src/server/GarbageCollector.ts6
-rw-r--r--src/server/IDatabase.ts4
-rw-r--r--src/server/MemoryDatabase.ts23
-rw-r--r--src/server/Websocket/Websocket.ts37
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts47
-rw-r--r--src/server/authentication/models/current_user_utils.ts36
-rw-r--r--src/server/authentication/models/user_model.ts4
-rw-r--r--src/server/database.ts118
-rw-r--r--src/server/remapUrl.ts4
16 files changed, 173 insertions, 220 deletions
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index 9e70af2eb..bd80d6500 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -1,12 +1,12 @@
import ApiManager, { Registration } from "./ApiManager";
-import { Method, _permission_denied, PublicHandler } from "../RouteManager";
+import { Method, _permission_denied } from "../RouteManager";
import { WebSocket } from "../Websocket/Websocket";
import { Database } from "../database";
import rimraf = require("rimraf");
-import { pathToDirectory, Directory } from "./UploadManager";
import { filesDirectory } from "..";
import { DashUploadUtils } from "../DashUploadUtils";
import { mkdirSync } from "fs";
+import RouteSubscriber from "../RouteSubscriber";
export default class DeleteManager extends ApiManager {
@@ -14,68 +14,39 @@ export default class DeleteManager extends ApiManager {
register({
method: Method.GET,
- subscription: "/delete",
- secureHandler: async ({ res, isRelease }) => {
+ subscription: new RouteSubscriber("delete").add("target?"),
+ secureHandler: async ({ req, res, isRelease }) => {
if (isRelease) {
- return _permission_denied(res, deletionPermissionError);
+ return _permission_denied(res, "Cannot perform a delete operation outside of the development environment!");
}
- await WebSocket.deleteFields();
- res.redirect("/home");
- }
- });
- register({
- method: Method.GET,
- subscription: "/deleteAll",
- secureHandler: async ({ res, isRelease }) => {
- if (isRelease) {
- return _permission_denied(res, deletionPermissionError);
+ const { target } = req.params;
+ const { doDelete } = WebSocket;
+
+ if (!target) {
+ await doDelete();
+ } else {
+ let all = false;
+ switch (target) {
+ case "all":
+ all = true;
+ case "database":
+ await doDelete(false);
+ if (!all) break;
+ case "files":
+ rimraf.sync(filesDirectory);
+ mkdirSync(filesDirectory);
+ await DashUploadUtils.buildFileDirectories();
+ break;
+ default:
+ await Database.Instance.dropSchema(target);
+ }
}
- await WebSocket.deleteAll();
- res.redirect("/home");
- }
- });
- register({
- method: Method.GET,
- subscription: "/deleteAssets",
- secureHandler: async ({ res, isRelease }) => {
- if (isRelease) {
- return _permission_denied(res, deletionPermissionError);
- }
- rimraf.sync(filesDirectory);
- mkdirSync(filesDirectory);
- await DashUploadUtils.buildFileDirectories();
- res.redirect("/delete");
- }
- });
-
- register({
- method: Method.GET,
- subscription: "/deleteWithAux",
- secureHandler: async ({ res, isRelease }) => {
- if (isRelease) {
- return _permission_denied(res, deletionPermissionError);
- }
- await Database.Auxiliary.DeleteAll();
- res.redirect("/delete");
- }
- });
-
- register({
- method: Method.GET,
- subscription: "/deleteWithGoogleCredentials",
- secureHandler: async ({ res, isRelease }) => {
- if (isRelease) {
- return _permission_denied(res, deletionPermissionError);
- }
- await Database.Auxiliary.GoogleAuthenticationToken.DeleteAll();
- res.redirect("/delete");
+ res.redirect("/home");
}
});
}
-}
-
-const deletionPermissionError = "Cannot perform a delete operation outside of the development environment!";
+} \ No newline at end of file
diff --git a/src/server/ApiManagers/GeneralGoogleManager.ts b/src/server/ApiManagers/GeneralGoogleManager.ts
index a5240edbc..17968cc7d 100644
--- a/src/server/ApiManagers/GeneralGoogleManager.ts
+++ b/src/server/ApiManagers/GeneralGoogleManager.ts
@@ -1,10 +1,8 @@
import ApiManager, { Registration } from "./ApiManager";
import { Method, _permission_denied } from "../RouteManager";
import { GoogleApiServerUtils } from "../apis/google/GoogleApiServerUtils";
-import { Database } from "../database";
import RouteSubscriber from "../RouteSubscriber";
-
-const deletionPermissionError = "Cannot perform specialized delete outside of the development environment!";
+import { Database } from "../database";
const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerUtils.ApiRouter>([
["create", (api, params) => api.create(params)],
@@ -20,11 +18,11 @@ export default class GeneralGoogleManager extends ApiManager {
method: Method.GET,
subscription: "/readGoogleAccessToken",
secureHandler: async ({ user, res }) => {
- const token = await GoogleApiServerUtils.retrieveAccessToken(user.id);
- if (!token) {
+ const { credentials } = (await GoogleApiServerUtils.retrieveCredentials(user.id));
+ if (!credentials?.access_token) {
return res.send(GoogleApiServerUtils.generateAuthenticationUrl());
}
- return res.send(token);
+ return res.send(credentials);
}
});
@@ -37,6 +35,15 @@ export default class GeneralGoogleManager extends ApiManager {
});
register({
+ method: Method.GET,
+ subscription: "/revokeGoogleAccessToken",
+ secureHandler: async ({ user, res }) => {
+ await Database.Auxiliary.GoogleAuthenticationToken.Revoke(user.id);
+ res.send();
+ }
+ });
+
+ register({
method: Method.POST,
subscription: new RouteSubscriber("googleDocs").add("sector", "action"),
secureHandler: async ({ req, res, user }) => {
diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts
index 88219423d..11841a603 100644
--- a/src/server/ApiManagers/GooglePhotosManager.ts
+++ b/src/server/ApiManagers/GooglePhotosManager.ts
@@ -56,7 +56,7 @@ export default class GooglePhotosManager extends ApiManager {
const { media } = req.body;
// first we need to ensure that we know the google account to which these photos will be uploaded
- const token = await GoogleApiServerUtils.retrieveAccessToken(user.id);
+ const token = (await GoogleApiServerUtils.retrieveCredentials(user.id))?.credentials?.access_token;
if (!token) {
return _error(res, authenticationError);
}
diff --git a/src/server/ApiManagers/SessionManager.ts b/src/server/ApiManagers/SessionManager.ts
index bcaa6598f..fa2f6002a 100644
--- a/src/server/ApiManagers/SessionManager.ts
+++ b/src/server/ApiManagers/SessionManager.ts
@@ -55,7 +55,7 @@ export default class SessionManager extends ApiManager {
register({
method: Method.GET,
- subscription: this.secureSubscriber("delete"),
+ subscription: this.secureSubscriber("deleteSession"),
secureHandler: this.authorizedAction(async ({ res }) => {
const { error } = await sessionAgent.serverWorker.emit("delete");
res.send(error ? error.message : "Your request was successful: the server successfully deleted the database. Return to /home.");
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 98f029c7d..b185d3b55 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -171,7 +171,7 @@ export default class UploadManager extends ApiManager {
await Promise.all(docs.map((doc: any) => new Promise(res => Database.Instance.replace(doc.id, doc, (err, r) => {
err && console.log(err);
res();
- }, true, "newDocuments"))));
+ }, true))));
} catch (e) { console.log(e); }
unlink(path_2, () => { });
}
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index d9d346cc1..68b3107ae 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -89,8 +89,6 @@ export default class UserManager extends ApiManager {
}
});
-
-
register({
method: Method.GET,
subscription: "/activity",
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index 5cbba13de..ef9b88541 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -37,7 +37,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
monitor.addReplCommand("debug", [/\S+\@\S+/], async ([to]) => this.dispatchZippedDebugBackup(to));
monitor.on("backup", this.backup);
monitor.on("debug", async ({ to }) => this.dispatchZippedDebugBackup(to));
- monitor.on("delete", WebSocket.deleteFields);
+ monitor.on("delete", WebSocket.doDelete);
monitor.coreHooks.onCrashDetected(this.dispatchCrashReport);
return sessionKey;
}
diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts
index 5729c3ee5..24745cbb4 100644
--- a/src/server/GarbageCollector.ts
+++ b/src/server/GarbageCollector.ts
@@ -76,7 +76,7 @@ async function GarbageCollect(full: boolean = true) {
if (!fetchIds.length) {
continue;
}
- const docs = await new Promise<{ [key: string]: any }[]>(res => Database.Instance.getDocuments(fetchIds, res, "newDocuments"));
+ const docs = await new Promise<{ [key: string]: any }[]>(res => Database.Instance.getDocuments(fetchIds, res));
for (const doc of docs) {
const id = doc.id;
if (doc === undefined) {
@@ -116,10 +116,10 @@ async function GarbageCollect(full: boolean = true) {
const count = Math.min(toDelete.length, 5000);
const toDeleteDocs = toDelete.slice(i, i + count);
i += count;
- const result = await Database.Instance.delete({ _id: { $in: toDeleteDocs } }, "newDocuments");
+ const result = await Database.Instance.delete({ _id: { $in: toDeleteDocs } });
deleted += result.deletedCount || 0;
}
- // const result = await Database.Instance.delete({ _id: { $in: toDelete } }, "newDocuments");
+ // const result = await Database.Instance.delete({ _id: { $in: toDelete } });
console.log(`${deleted} documents deleted`);
await Search.deleteDocuments(toDelete);
diff --git a/src/server/IDatabase.ts b/src/server/IDatabase.ts
index 6a63df485..dd4968579 100644
--- a/src/server/IDatabase.ts
+++ b/src/server/IDatabase.ts
@@ -2,7 +2,6 @@ import * as mongodb from 'mongodb';
import { Transferable } from './Message';
export const DocumentsCollection = 'documents';
-export const NewDocumentsCollection = 'newDocuments';
export interface IDatabase {
update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert?: boolean, collectionName?: string): Promise<void>;
updateMany(query: any, update: any, collectionName?: string): Promise<mongodb.WriteOpResult>;
@@ -12,12 +11,13 @@ export interface IDatabase {
delete(query: any, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>;
delete(id: string, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>;
- deleteAll(collectionName?: string, persist?: boolean): Promise<any>;
+ dropSchema(...schemaNames: string[]): Promise<any>;
insert(value: any, collectionName?: string): Promise<void>;
getDocument(id: string, fn: (result?: Transferable) => void, collectionName?: string): void;
getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName?: string): void;
+ getCollectionNames(): Promise<string[]>;
visit(ids: string[], fn: (result: any) => string[] | Promise<string[]>, collectionName?: string): Promise<void>;
query(query: { [key: string]: any }, projection?: { [key: string]: 0 | 1 }, collectionName?: string): Promise<mongodb.Cursor>;
diff --git a/src/server/MemoryDatabase.ts b/src/server/MemoryDatabase.ts
index 543f96e7f..1f1d702d9 100644
--- a/src/server/MemoryDatabase.ts
+++ b/src/server/MemoryDatabase.ts
@@ -1,4 +1,4 @@
-import { IDatabase, DocumentsCollection, NewDocumentsCollection } from './IDatabase';
+import { IDatabase, DocumentsCollection } from './IDatabase';
import { Transferable } from './Message';
import * as mongodb from 'mongodb';
@@ -15,6 +15,10 @@ export class MemoryDatabase implements IDatabase {
}
}
+ public getCollectionNames() {
+ return Promise.resolve(Object.keys(this.db));
+ }
+
public update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, _upsert?: boolean, collectionName = DocumentsCollection): Promise<void> {
const collection = this.getCollection(collectionName);
const set = "$set";
@@ -41,7 +45,7 @@ export class MemoryDatabase implements IDatabase {
return Promise.resolve(undefined);
}
- public updateMany(query: any, update: any, collectionName = NewDocumentsCollection): Promise<mongodb.WriteOpResult> {
+ public updateMany(query: any, update: any, collectionName = DocumentsCollection): Promise<mongodb.WriteOpResult> {
throw new Error("Can't updateMany a MemoryDatabase");
}
@@ -58,8 +62,15 @@ export class MemoryDatabase implements IDatabase {
return Promise.resolve({} as any);
}
- public deleteAll(collectionName = DocumentsCollection, _persist = true): Promise<any> {
- delete this.db[collectionName];
+ public async dropSchema(...schemaNames: string[]): Promise<any> {
+ const existing = await this.getCollectionNames();
+ let valid: string[];
+ if (schemaNames.length) {
+ valid = schemaNames.filter(collection => existing.includes(collection));
+ } else {
+ valid = existing;
+ }
+ valid.forEach(schemaName => delete this.db[schemaName]);
return Promise.resolve();
}
@@ -69,14 +80,14 @@ export class MemoryDatabase implements IDatabase {
return Promise.resolve();
}
- public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = NewDocumentsCollection): void {
+ public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = DocumentsCollection): void {
fn(this.getCollection(collectionName)[id]);
}
public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = DocumentsCollection): void {
fn(ids.map(id => this.getCollection(collectionName)[id]));
}
- public async visit(ids: string[], fn: (result: any) => string[] | Promise<string[]>, collectionName = NewDocumentsCollection): Promise<void> {
+ public async visit(ids: string[], fn: (result: any) => string[] | Promise<string[]>, collectionName = DocumentsCollection): Promise<void> {
const visited = new Set<string>();
while (ids.length) {
const count = Math.min(ids.length, 1000);
diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts
index be895c4bc..844535056 100644
--- a/src/server/Websocket/Websocket.ts
+++ b/src/server/Websocket/Websocket.ts
@@ -12,6 +12,7 @@ import { timeMap } from "../ApiManagers/UserManager";
import { green } from "colors";
import { networkInterfaces } from "os";
import executeImport from "../../scraping/buxton/final/BuxtonImporter";
+import { DocumentsCollection } from "../IDatabase";
export namespace WebSocket {
@@ -96,7 +97,7 @@ export namespace WebSocket {
Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField);
Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields);
if (isRelease) {
- Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields);
+ Utils.AddServerHandler(socket, MessageStore.DeleteAll, () => doDelete(false));
}
Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField);
@@ -163,24 +164,10 @@ export namespace WebSocket {
}
}
- export async function deleteFields() {
- await Database.Instance.deleteAll();
- if (process.env.DISABLE_SEARCH !== "true") {
- await Search.clear();
- }
- await Database.Instance.deleteAll('newDocuments');
- }
-
- // export async function deleteUserDocuments() {
- // await Database.Instance.deleteAll();
- // await Database.Instance.deleteAll('newDocuments');
- // }
-
- export async function deleteAll() {
- await Database.Instance.deleteAll();
- await Database.Instance.deleteAll('newDocuments');
- await Database.Instance.deleteAll('sessions');
- await Database.Instance.deleteAll('users');
+ export async function doDelete(onlyFields = true) {
+ const target: string[] = [];
+ onlyFields && target.push(DocumentsCollection);
+ await Database.Instance.dropSchema(...target);
if (process.env.DISABLE_SEARCH !== "true") {
await Search.clear();
}
@@ -210,11 +197,11 @@ export namespace WebSocket {
}
function GetRefField([id, callback]: [string, (result?: Transferable) => void]) {
- Database.Instance.getDocument(id, callback, "newDocuments");
+ Database.Instance.getDocument(id, callback);
}
function GetRefFields([ids, callback]: [string[], (result?: Transferable[]) => void]) {
- Database.Instance.getDocuments(ids, callback, "newDocuments");
+ Database.Instance.getDocuments(ids, callback);
}
const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = {
@@ -271,7 +258,7 @@ export namespace WebSocket {
function UpdateField(socket: Socket, diff: Diff) {
Database.Instance.update(diff.id, diff.diff,
- () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false, "newDocuments");
+ () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false);
const docfield = diff.diff.$set || diff.diff.$unset;
if (!docfield) {
return;
@@ -296,7 +283,7 @@ export namespace WebSocket {
}
function DeleteField(socket: Socket, id: string) {
- Database.Instance.delete({ _id: id }, "newDocuments").then(() => {
+ Database.Instance.delete({ _id: id }).then(() => {
socket.broadcast.emit(MessageStore.DeleteField.Message, id);
});
@@ -304,14 +291,14 @@ export namespace WebSocket {
}
function DeleteFields(socket: Socket, ids: string[]) {
- Database.Instance.delete({ _id: { $in: ids } }, "newDocuments").then(() => {
+ Database.Instance.delete({ _id: { $in: ids } }).then(() => {
socket.broadcast.emit(MessageStore.DeleteFields.Message, ids);
});
Search.deleteDocuments(ids);
}
function CreateField(newValue: any) {
- Database.Instance.insert(newValue, "newDocuments");
+ Database.Instance.insert(newValue);
}
}
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 0f75833ee..48a8da89f 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -149,26 +149,6 @@ export namespace GoogleApiServerUtils {
}
/**
- * Returns the lengthy string or access token that can be passed into
- * the headers of an API request or into the constructor of the Photos
- * client API wrapper.
- * @param userId the Dash user id of the user requesting his/her associated
- * access_token
- * @returns the current access_token associated with the requesting
- * Dash user. The access_token is valid for only an hour, and
- * is then refreshed.
- */
- export async function retrieveAccessToken(userId: string): Promise<string> {
- return new Promise(async resolve => {
- const { credentials } = await retrieveCredentials(userId);
- if (!credentials) {
- return resolve();
- }
- resolve(credentials.access_token!);
- });
- }
-
- /**
* Manipulates a mapping such that, in the limit, each Dash user has
* an associated authenticated OAuth2 client at their disposal. This
* function ensures that the client's credentials always remain up to date
@@ -217,18 +197,6 @@ export namespace GoogleApiServerUtils {
}
/**
- * This is what we return to the server in processNewUser(), after the
- * worker OAuth2Client has used the user-pasted authentication code
- * to retrieve an access token and an info token. The avatar is the
- * URL to the Google-hosted mono-color, single white letter profile 'image'.
- */
- export interface GoogleAuthenticationResult {
- access_token: string;
- avatar: string;
- name: string;
- }
-
- /**
* This method receives the authentication code that the
* user pasted into the overlay in the client side and uses the worker
* and the authentication code to fetch the full set of credentials that
@@ -245,7 +213,7 @@ export namespace GoogleApiServerUtils {
* and display basic user information in the overlay on successful authentication.
* This can be expanded as needed by adding properties to the interface GoogleAuthenticationResult.
*/
- export async function processNewUser(userId: string, authenticationCode: string): Promise<GoogleAuthenticationResult> {
+ export async function processNewUser(userId: string, authenticationCode: string): Promise<EnrichedCredentials> {
const credentials = await new Promise<Credentials>((resolve, reject) => {
worker.getToken(authenticationCode, async (err, credentials) => {
if (err || !credentials) {
@@ -257,12 +225,7 @@ export namespace GoogleApiServerUtils {
});
const enriched = injectUserInfo(credentials);
await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, enriched);
- const { given_name, picture } = enriched.userInfo;
- return {
- access_token: enriched.access_token!,
- avatar: picture,
- name: given_name
- };
+ return enriched;
}
/**
@@ -316,15 +279,15 @@ export namespace GoogleApiServerUtils {
* @returns the credentials, or undefined if the user has no stored associated credentials,
* and a flag indicating whether or not they were refreshed during retrieval
*/
- async function retrieveCredentials(userId: string): Promise<{ credentials: Opt<Credentials>, refreshed: boolean }> {
- let credentials: Opt<Credentials> = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId);
+ export async function retrieveCredentials(userId: string): Promise<{ credentials: Opt<EnrichedCredentials>, refreshed: boolean }> {
+ let credentials = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId);
let refreshed = false;
if (!credentials) {
return { credentials: undefined, refreshed };
}
// check for token expiry
if (credentials.expiry_date! <= new Date().getTime()) {
- credentials = await refreshAccessToken(credentials, userId);
+ credentials = { ...credentials, ...(await refreshAccessToken(credentials, userId)) };
refreshed = true;
}
return { credentials, refreshed };
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index d3e085b21..e7b448358 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -306,7 +306,7 @@ export class CurrentUserUtils {
{ _width: 250, _height: 250, title: "container" });
}
if (doc.emptyWebpage === undefined) {
- doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600 })
+ doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600 });
}
return [
{ title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
@@ -328,7 +328,8 @@ export class CurrentUserUtils {
// { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.inkPen = sameDocs(this.activePen.inkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "pink", activePen: doc },
// { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.inkPen = this;', ischecked: `sameDocs(this.activePen.inkPen, this)`, backgroundColor: "white", activePen: doc },
{ title: "Drag a document previewer", label: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.emptyDocHolder as Doc },
- { title: "Drag a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
+ { title: "Toggle a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
+ { title: "Connect a Google Account", label: "Google Account", icon: "external-link-alt", click: 'GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' },
];
}
@@ -344,21 +345,22 @@ export class CurrentUserUtils {
alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
}
}
- const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)).
- map(data => Docs.Create.FontIconDocument({
- _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
- icon: data.icon,
- title: data.title,
- label: data.label,
- ignoreClick: data.ignoreClick,
- dropAction: "copy",
- onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
- onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
- ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined,
- activePen: data.activePen,
- backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]),
- dragFactory: data.dragFactory,
- }));
+ const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title));
+ const creatorBtns = buttons.map(({ title, label, icon, ignoreClick, drag, click, ischecked, activePen, backgroundColor, dragFactory }) => Docs.Create.FontIconDocument({
+ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100,
+ icon,
+ title,
+ label,
+ ignoreClick,
+ dropAction: "copy",
+ onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined,
+ onClick: click ? ScriptField.MakeScript(click) : undefined,
+ ischecked: ischecked ? ComputedField.MakeFunction(ischecked) : undefined,
+ activePen,
+ backgroundColor,
+ removeDropProperties: new List<string>(["dropAction"]),
+ dragFactory,
+ }));
if (dragCreatorSet === undefined) {
doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts
index 78e39dbc1..a0b688328 100644
--- a/src/server/authentication/models/user_model.ts
+++ b/src/server/authentication/models/user_model.ts
@@ -74,9 +74,9 @@ userSchema.pre("save", function save(next) {
const comparePassword: comparePasswordFunction = function (this: DashUserModel, candidatePassword, cb) {
// Choose one of the following bodies for authentication logic.
- // secure
+ // secure (expected, default)
bcrypt.compare(candidatePassword, this.password, cb);
- // bypass password
+ // bypass password (debugging)
// cb(undefined, true);
};
diff --git a/src/server/database.ts b/src/server/database.ts
index ad285765b..9ba461b65 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -4,7 +4,7 @@ import { Opt } from '../new_fields/Doc';
import { Utils, emptyFunction } from '../Utils';
import { Credentials } from 'google-auth-library';
import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils';
-import { IDatabase } from './IDatabase';
+import { IDatabase, DocumentsCollection } from './IDatabase';
import { MemoryDatabase } from './MemoryDatabase';
import * as mongoose from 'mongoose';
import { Upload } from './SharedMediaTypes';
@@ -14,7 +14,7 @@ export namespace Database {
export let disconnect: Function;
const schema = 'Dash';
const port = 27017;
- export const url = `mongodb://localhost:${port}/`;
+ export const url = `mongodb://localhost:${port}/${schema}`;
enum ConnectionStates {
disconnected = 0,
@@ -47,28 +47,29 @@ export namespace Database {
}
export class Database implements IDatabase {
- public static DocumentsCollection = 'documents';
private MongoClient = mongodb.MongoClient;
private currentWrites: { [id: string]: Promise<void> } = {};
private db?: mongodb.Db;
private onConnect: (() => void)[] = [];
- doConnect() {
+ async doConnect() {
console.error(`\nConnecting to Mongo with URL : ${url}\n`);
- this.MongoClient.connect(url, { connectTimeoutMS: 30000, socketTimeoutMS: 30000, useUnifiedTopology: true }, (_err, client) => {
- console.error("mongo connect response\n");
- if (!client) {
- console.error("\nMongo connect failed with the error:\n");
- console.log(_err);
- process.exit(0);
- }
- this.db = client.db();
- this.onConnect.forEach(fn => fn());
+ return new Promise<void>(resolve => {
+ this.MongoClient.connect(url, { connectTimeoutMS: 30000, socketTimeoutMS: 30000, useUnifiedTopology: true }, (_err, client) => {
+ console.error("mongo connect response\n");
+ if (!client) {
+ console.error("\nMongo connect failed with the error:\n");
+ console.log(_err);
+ process.exit(0);
+ }
+ this.db = client.db();
+ this.onConnect.forEach(fn => fn());
+ resolve();
+ });
});
}
- public async update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = Database.DocumentsCollection) {
-
+ public async update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = DocumentsCollection) {
if (this.db) {
const collection = this.db.collection(collectionName);
const prom = this.currentWrites[id];
@@ -93,7 +94,7 @@ export namespace Database {
}
}
- public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = Database.DocumentsCollection) {
+ public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = DocumentsCollection) {
if (this.db) {
const collection = this.db.collection(collectionName);
const prom = this.currentWrites[id];
@@ -117,9 +118,25 @@ export namespace Database {
}
}
+ public async getCollectionNames() {
+ const cursor = this.db?.listCollections();
+ const collectionNames: string[] = [];
+ if (cursor) {
+ while (await cursor.hasNext()) {
+ const collection: any = await cursor.next();
+ collection && collectionNames.push(collection.name);
+ }
+ }
+ return collectionNames;
+ }
+
+ public async clear() {
+ return Promise.all((await this.getCollectionNames()).map(collection => this.dropSchema(collection)));
+ }
+
public delete(query: any, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>;
public delete(id: string, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>;
- public delete(id: any, collectionName = Database.DocumentsCollection) {
+ public delete(id: any, collectionName = DocumentsCollection) {
if (typeof id === "string") {
id = { _id: id };
}
@@ -131,25 +148,26 @@ export namespace Database {
}
}
- public async deleteAll(collectionName = Database.DocumentsCollection, persist = true): Promise<any> {
- return new Promise(resolve => {
- const executor = async (database: mongodb.Db) => {
- if (persist) {
- await database.collection(collectionName).deleteMany({});
- } else {
- await database.dropCollection(collectionName);
- }
- resolve();
- };
- if (this.db) {
- executor(this.db);
+ public async dropSchema(...targetSchemas: string[]): Promise<any> {
+ const executor = async (database: mongodb.Db) => {
+ const existing = await Instance.getCollectionNames();
+ let valid: string[];
+ if (targetSchemas.length) {
+ valid = targetSchemas.filter(collection => existing.includes(collection));
} else {
- this.onConnect.push(() => this.db && executor(this.db));
+ valid = existing;
}
- });
+ const pending = Promise.all(valid.map(schemaName => database.dropCollection(schemaName)));
+ return (await pending).every(dropOutcome => dropOutcome);
+ };
+ if (this.db) {
+ return executor(this.db);
+ } else {
+ this.onConnect.push(() => this.db && executor(this.db));
+ }
}
- public async insert(value: any, collectionName = Database.DocumentsCollection) {
+ public async insert(value: any, collectionName = DocumentsCollection) {
if (this.db) {
if ("id" in value) {
value._id = value.id;
@@ -177,7 +195,7 @@ export namespace Database {
}
}
- public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = "newDocuments") {
+ public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = DocumentsCollection) {
if (this.db) {
this.db.collection(collectionName).findOne({ _id: id }, (err, result) => {
if (result) {
@@ -193,7 +211,7 @@ export namespace Database {
}
}
- public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) {
+ public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = DocumentsCollection) {
if (this.db) {
this.db.collection(collectionName).find({ _id: { "$in": ids } }).toArray((err, docs) => {
if (err) {
@@ -211,7 +229,7 @@ export namespace Database {
}
}
- public async visit(ids: string[], fn: (result: any) => string[] | Promise<string[]>, collectionName = "newDocuments"): Promise<void> {
+ public async visit(ids: string[], fn: (result: any) => string[] | Promise<string[]>, collectionName = DocumentsCollection): Promise<void> {
if (this.db) {
const visited = new Set<string>();
while (ids.length) {
@@ -238,7 +256,7 @@ export namespace Database {
}
}
- public query(query: { [key: string]: any }, projection?: { [key: string]: 0 | 1 }, collectionName = "newDocuments"): Promise<mongodb.Cursor> {
+ public query(query: { [key: string]: any }, projection?: { [key: string]: 0 | 1 }, collectionName = DocumentsCollection): Promise<mongodb.Cursor> {
if (this.db) {
let cursor = this.db.collection(collectionName).find(query);
if (projection) {
@@ -252,7 +270,7 @@ export namespace Database {
}
}
- public updateMany(query: any, update: any, collectionName = "newDocuments") {
+ public updateMany(query: any, update: any, collectionName = DocumentsCollection) {
if (this.db) {
const db = this.db;
return new Promise<mongodb.WriteOpResult>(res => db.collection(collectionName).update(query, update, (_, result) => res(result)));
@@ -282,7 +300,8 @@ export namespace Database {
export namespace Auxiliary {
export enum AuxiliaryCollections {
- GooglePhotosUploadHistory = "uploadedFromGooglePhotos"
+ GooglePhotosUploadHistory = "uploadedFromGooglePhotos",
+ GoogleAuthentication = "googleAuthentication"
}
const SanitizedCappedQuery = async (query: { [key: string]: any }, collection: string, cap: number, removeId = true) => {
@@ -306,27 +325,30 @@ export namespace Database {
export namespace GoogleAuthenticationToken {
- const GoogleAuthentication = "googleAuthentication";
-
- export type StoredCredentials = Credentials & { _id: string };
+ type StoredCredentials = GoogleApiServerUtils.EnrichedCredentials & { _id: string };
export const Fetch = async (userId: string, removeId = true): Promise<Opt<StoredCredentials>> => {
- return SanitizedSingletonQuery<StoredCredentials>({ userId }, GoogleAuthentication, removeId);
+ return SanitizedSingletonQuery<StoredCredentials>({ userId }, AuxiliaryCollections.GoogleAuthentication, removeId);
};
export const Write = async (userId: string, enrichedCredentials: GoogleApiServerUtils.EnrichedCredentials) => {
- return Instance.insert({ userId, canAccess: [], ...enrichedCredentials }, GoogleAuthentication);
+ return Instance.insert({ userId, canAccess: [], ...enrichedCredentials }, AuxiliaryCollections.GoogleAuthentication);
};
export const Update = async (userId: string, access_token: string, expiry_date: number) => {
const entry = await Fetch(userId, false);
if (entry) {
const parameters = { $set: { access_token, expiry_date } };
- return Instance.update(entry._id, parameters, emptyFunction, true, GoogleAuthentication);
+ return Instance.update(entry._id, parameters, emptyFunction, true, AuxiliaryCollections.GoogleAuthentication);
}
};
- export const DeleteAll = () => Instance.deleteAll(GoogleAuthentication, false);
+ export const Revoke = async (userId: string) => {
+ const entry = await Fetch(userId, false);
+ if (entry) {
+ Instance.delete({ _id: entry._id }, AuxiliaryCollections.GoogleAuthentication);
+ }
+ };
}
@@ -338,12 +360,6 @@ export namespace Database {
return Instance.insert(bundle, AuxiliaryCollections.GooglePhotosUploadHistory);
};
- export const DeleteAll = async (persist = false) => {
- const collectionNames = Object.values(AuxiliaryCollections);
- const pendingDeletions = collectionNames.map(name => Instance.deleteAll(name, persist));
- return Promise.all(pendingDeletions);
- };
-
}
}
diff --git a/src/server/remapUrl.ts b/src/server/remapUrl.ts
index 45d2fdd33..91a3cb6bf 100644
--- a/src/server/remapUrl.ts
+++ b/src/server/remapUrl.ts
@@ -1,6 +1,4 @@
import { Database } from "./database";
-import { Search } from "./Search";
-import * as path from 'path';
//npx ts-node src/server/remapUrl.ts
@@ -50,7 +48,7 @@ async function update() {
return new Promise(res => Database.Instance.update(doc[0], doc[1], () => {
console.log("wrote " + JSON.stringify(doc[1]));
res();
- }, false, "newDocuments"));
+ }, false));
}));
console.log("Done");
// await Promise.all(updates.map(update => {