From 3b0fca96e8350c4d103833a0cee6dcdc5e781d5c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Jul 2025 13:03:12 -0400
Subject: fixed placement of Docs on views that are modified as the result of
 the AI componentUI view being display

---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b45409a75..49c684ab1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -2164,10 +2164,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @observable private _regenerateLoading = false;
     @observable private _regenInput = '';
-    @observable private _canInteract = true;
-    @observable private _drawingFillInput = '';
-    @observable private _regenLoading = false;
-    @observable private _drawingFillLoading = false;
     @observable private _fireflyRefStrength = 0;
 
     componentAIView = () => {
@@ -2180,7 +2176,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         aria-label="Edit instructions input"
                         type="text"
                         value={this._regenInput || StrCast(this.Document.title)}
-                        onChange={action(e => this._canInteract && (this._regenInput = e.target.value))}
+                        onChange={action(e => (this._regenInput = e.target.value))}
                         placeholder={this._regenInput || StrCast(this.Document.title)}
                     />
                     <div className="imageBox-aiView-regenerate-createBtn">
@@ -2213,10 +2209,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                             max={100}
                             number={this._fireflyRefStrength}
                             size={Size.XXSMALL}
-                            setNumber={undoable(
-                                action(val => this._canInteract && (this._fireflyRefStrength = val as number)),
-                                `${this.Document.title} button set from list`
-                            )}
+                            setNumber={undoable(action(val => (this._fireflyRefStrength = val as number)),`${this.Document.title} button set from list` )}
                             fillWidth
                         />
                     </div>
-- 
cgit v1.2.3-70-g09d2


From 401431d732fecd6bdef69d83e21253e1b157416d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 21 Jul 2025 12:45:55 -0400
Subject: cleaning up server warnings/errors

---
 src/client/views/GestureOverlay.tsx                |  2 -
 src/client/views/PropertiesView.tsx                |  1 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  1 -
 src/client/views/nodes/ImageBox.tsx                |  1 -
 src/client/views/smartdraw/SmartDrawHandler.tsx    |  2 +-
 src/server/ApiManagers/AssistantManager.ts         |  4 +-
 src/server/ApiManagers/AzureManager.ts             | 43 +++++------
 src/server/ApiManagers/UserManager.ts              | 29 ++++----
 src/server/RouteManager.ts                         | 46 +++++++-----
 src/server/authentication/AuthenticationManager.ts | 44 +++++------
 src/server/authentication/DashUserModel.ts         | 87 +++++++++++-----------
 src/server/authentication/Passport.ts              | 13 ++--
 12 files changed, 139 insertions(+), 134 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 8488c5293..113250a99 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -50,9 +50,7 @@ interface GestureOverlayProps {
  * drew or perform the gesture's action
  */
 export class GestureOverlay extends ObservableReactComponent<React.PropsWithChildren<GestureOverlayProps>> {
-    // eslint-disable-next-line no-use-before-define
     static Instance: GestureOverlay;
-    // eslint-disable-next-line no-use-before-define
     static Instances: GestureOverlay[] = [];
 
     @observable public SavedColor?: string = undefined;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5211272b0..f7c4b464c 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -58,7 +58,6 @@ interface PropertiesViewProps {
 export class PropertiesView extends ObservableReactComponent<PropertiesViewProps> {
     private _widthUndo?: UndoManager.Batch;
 
-    // eslint-disable-next-line no-use-before-define
     public static Instance: PropertiesView;
     constructor(props: PropertiesViewProps) {
         super(props);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 49c684ab1..73856f47b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -105,7 +105,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     /**
      * The Freeformview below the cursor at the start of a gesture (that receives the pointerDown event).  Used by GestureOverlay to determine the doc a gesture should apply to.
      */
-    // eslint-disable-next-line no-use-before-define
     public static DownFfview: CollectionFreeFormView | undefined; // the first DocView that receives a pointerdown event.  used by GestureOverlay to determine the doc a gesture should apply to.
 
     private _clusters = new CollectionFreeFormClusters(this);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 4886f0e96..617a09ed5 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -49,7 +49,6 @@ import { gptImageLabel } from '../../apis/gpt/GPT';
 
 const DefaultPath = '/assets/unknown-file-icon-hi.png';
 export class ImageEditorData {
-    // eslint-disable-next-line no-use-before-define
     private static _instance: ImageEditorData;
     private static get imageData() { return (ImageEditorData._instance ?? new ImageEditorData()).imageData; } // prettier-ignore
     @observable imageData: { rootDoc: Doc | undefined; open: boolean; source: string; addDoc: Opt<(doc: Doc | Doc[], annotationKey?: string) => boolean> } = observable({ rootDoc: undefined, open: false, source: '', addDoc: undefined });
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index b7ff5fff7..a017dd7eb 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -450,7 +450,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
                         },
                     }}
                     checked={this._generateDrawing}
-                    onChange={() => this._canInteract && (this._generateDrawing = !this._generateDrawing)}
+                    onChange={action(() => this._canInteract && (this._generateDrawing = !this._generateDrawing))}
                 />
             </div>
             <div className="image-checkbox">
diff --git a/src/server/ApiManagers/AssistantManager.ts b/src/server/ApiManagers/AssistantManager.ts
index af25722a4..20ec0dfe6 100644
--- a/src/server/ApiManagers/AssistantManager.ts
+++ b/src/server/ApiManagers/AssistantManager.ts
@@ -439,9 +439,9 @@ export default class AssistantManager extends ApiManager {
                 try {
                     const image = await openai.images.generate({ model: 'dall-e-3', prompt: image_prompt, response_format: 'url' });
                     console.log(image);
-                    const result = await DashUploadUtils.UploadImage(image.data[0].url!);
+                    const url = image.data?.[0].url;
 
-                    const url = image.data[0].url;
+                    const result = url ? await DashUploadUtils.UploadImage(url) : { error: 'Image generation failed' };
 
                     res.send({ result, url });
                 } catch (error) {
diff --git a/src/server/ApiManagers/AzureManager.ts b/src/server/ApiManagers/AzureManager.ts
index 2d0ab3aa6..d54b15810 100644
--- a/src/server/ApiManagers/AzureManager.ts
+++ b/src/server/ApiManagers/AzureManager.ts
@@ -1,37 +1,38 @@
-import { ContainerClient, BlobServiceClient } from "@azure/storage-blob";
-import * as fs from "fs";
-import { Readable, Stream } from "stream";
-import * as path from "path";
+import { ContainerClient, BlobServiceClient } from '@azure/storage-blob';
+import * as fs from 'fs';
+import { Readable } from 'stream';
+import * as path from 'path';
 const AZURE_STORAGE_CONNECTION_STRING = process.env.AZURE_STORAGE_CONNECTION_STRING;
 
 const extToType: { [suffix: string]: string } = {
-    ".jpeg" : "image/jpeg",
-    ".jpg" : "image/jpeg",
-    ".png" : "image/png",
-    ".svg" : "image/svg+xml",
-    ".webp" : "image/webp",
-    ".gif" : "image/gif"
-}
+    '.jpeg': 'image/jpeg',
+    '.jpg': 'image/jpeg',
+    '.png': 'image/png',
+    '.svg': 'image/svg+xml',
+    '.webp': 'image/webp',
+    '.gif': 'image/gif',
+};
 
 export class AzureManager {
     private _containerClient: ContainerClient;
     private _blobServiceClient: BlobServiceClient;
     private static _instance: AzureManager | undefined;
 
-    public static CONTAINER_NAME = "dashmedia";
-    public static STORAGE_ACCOUNT_NAME = "dashblobstore";
+    public static CONTAINER_NAME = 'dashmedia';
+    public static STORAGE_ACCOUNT_NAME = 'dashblobstore';
     public static BASE_STRING = `https://${AzureManager.STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${AzureManager.CONTAINER_NAME}`;
 
     constructor() {
         if (!AZURE_STORAGE_CONNECTION_STRING) {
-            throw new Error("Azure Storage Connection String Not Found");
+            throw new Error('Azure Storage Connection String Not Found');
         }
         this._blobServiceClient = BlobServiceClient.fromConnectionString(AZURE_STORAGE_CONNECTION_STRING);
         this._containerClient = this.BlobServiceClient.getContainerClient(AzureManager.CONTAINER_NAME);
     }
 
     public static get Instance() {
-        return this._instance = this._instance ?? new AzureManager();
+        this._instance = this._instance ?? new AzureManager();
+        return this._instance;
     }
 
     public get BlobServiceClient() {
@@ -44,14 +45,14 @@ export class AzureManager {
 
     public static UploadBlob(filename: string, filepath: string, filetype: string) {
         const blockBlobClient = this.Instance.ContainerClient.getBlockBlobClient(filename);
-        const blobOptions = { blobHTTPHeaders: { blobContentType: filetype }};
+        const blobOptions = { blobHTTPHeaders: { blobContentType: filetype } };
         const stream = fs.createReadStream(filepath);
         return blockBlobClient.uploadStream(stream, undefined, undefined, blobOptions);
     }
 
     public static UploadBase64ImageBlob(filename: string, data: string, filetype?: string) {
         const confirmedFiletype = filetype ? filetype : extToType[path.extname(filename)];
-        const buffer = Buffer.from(data, "base64");
+        const buffer = Buffer.from(data, 'base64');
         const blockBlobClient = this.Instance.ContainerClient.getBlockBlobClient(filename);
         const blobOptions = { blobHTTPHeaders: { blobContentType: confirmedFiletype } };
         return blockBlobClient.upload(buffer, buffer.length, blobOptions);
@@ -59,7 +60,7 @@ export class AzureManager {
 
     public static UploadBlobStream(stream: Readable, filename: string, filetype: string) {
         const blockBlobClient = this.Instance.ContainerClient.getBlockBlobClient(filename);
-        const blobOptions = { blobHTTPHeaders: { blobContentType: filetype }};
+        const blobOptions = { blobHTTPHeaders: { blobContentType: filetype } };
         return blockBlobClient.uploadStream(stream, undefined, undefined, blobOptions);
     }
 
@@ -74,9 +75,9 @@ export class AzureManager {
             console.log(`${blob.name}`);
 
             const blobItem = {
-                url : `https://${AzureManager.STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${AzureManager.CONTAINER_NAME}/${blob.name}`,
-                name : blob.name
-            }
+                url: `https://${AzureManager.STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${AzureManager.CONTAINER_NAME}/${blob.name}`,
+                name: blob.name,
+            };
 
             foundBlobs.push(blobItem);
         }
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index b587340e2..b42c974ac 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -22,6 +22,7 @@ export default class UserManager extends ApiManager {
             secureHandler: async ({ res }) => {
                 const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, 'users');
                 const results = await cursor.toArray();
+                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                 res.send(results.map((user: any) => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
             },
         });
@@ -31,9 +32,10 @@ export default class UserManager extends ApiManager {
             subscription: '/setCacheDocumentIds',
             secureHandler: async ({ user, req, res }) => {
                 const userModel = user;
-                const result: any = {};
+                const result: { error?: unknown } = {};
                 userModel.cacheDocumentIds = req.body.cacheDocumentIds;
-                userModel.save().then(undefined, (err: any) => {
+                // eslint-disable-next-line @typescript-eslint/no-explicit-any
+                userModel.save?.().then(undefined, (err: any) => {
                     if (err) {
                         result.error = [{ msg: 'Error while caching documents' }];
                     }
@@ -90,20 +92,19 @@ export default class UserManager extends ApiManager {
             subscription: '/internalResetPassword',
             secureHandler: async ({ user, req, res }) => {
                 const userModel = user;
-                const result: any = {};
-                // eslint-disable-next-line camelcase
+                const result: { error?: unknown } = {};
                 const { curr_pass, new_pass } = req.body;
                 // perhaps should assert whether curr password is entered correctly
                 const validated = await new Promise<Opt<boolean>>(resolve => {
-                    bcrypt.compare(curr_pass, userModel.password, (err, passwordsMatch) => {
-                        if (err || !passwordsMatch) {
-                            result.error = [{ msg: 'Incorrect current password' }];
-                            res.send(result);
-                            resolve(undefined);
-                        } else {
-                            resolve(passwordsMatch);
-                        }
-                    });
+                    userModel.password &&
+                        bcrypt.compare(curr_pass, userModel.password, (err, passwordsMatch) => {
+                            if (err || !passwordsMatch) {
+                                res.send({ error: [{ msg: 'Incorrect current password' }] });
+                                resolve(undefined);
+                            } else {
+                                resolve(passwordsMatch);
+                            }
+                        });
                 });
 
                 if (validated === undefined) {
@@ -133,7 +134,7 @@ export default class UserManager extends ApiManager {
                     userModel.passwordResetExpires = undefined;
                 }
 
-                userModel.save().then(undefined, err => {
+                userModel.save?.().then(undefined, err => {
                     if (err) {
                         result.error = [{ msg: 'Error while saving new password' }];
                     }
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index c5d70da3d..ed51aea40 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -18,10 +18,11 @@ export interface CoreArguments {
     isRelease: boolean;
 }
 
-export type AuthorizedCore = CoreArguments & { user: DashUserModel };
-export type SecureHandler = (core: AuthorizedCore) => any | Promise<any>;
-export type PublicHandler = (core: CoreArguments) => any | Promise<any>;
-export type ErrorHandler = (core: CoreArguments & { error: any }) => any | Promise<any>;
+export type AuthorizedCore = CoreArguments & { user: Partial<DashUserModel> };
+export type SecureHandler = (core: AuthorizedCore) => unknown | Promise<unknown>;
+export type PublicHandler = (core: CoreArguments) => unknown | Promise<unknown>;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export type ErrorHandler = (core: CoreArguments & { error: any }) => unknown | Promise<unknown>;
 
 export const STATUS = {
     OK: 200,
@@ -30,13 +31,14 @@ export const STATUS = {
     PERMISSION_DENIED: 403,
 };
 
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
 export function _error(res: Response, message: string, error?: any) {
     console.error(message, error);
     res.statusMessage = message;
     res.status(STATUS.EXECUTION_ERROR).send(error);
 }
 
-export function _success(res: Response, body: any) {
+export function _success(res: Response, body: unknown) {
     res.status(STATUS.OK).send(body);
 }
 
@@ -135,18 +137,6 @@ export default class RouteManager {
                 user = { id: 'guest', email: 'guest', userDocumentId: Utils.GuestID() };
             }
             const core = { req, res, isRelease };
-            const tryExecute = async (toExecute: (args: any) => any | Promise<any>, args: any) => {
-                try {
-                    await toExecute(args);
-                } catch (e) {
-                    console.log(red(target), user && 'email' in user ? '<user logged out>' : undefined);
-                    if (errorHandler) {
-                        errorHandler({ ...core, error: e });
-                    } else {
-                        _error(res, `The server encountered an internal error when serving ${target}.`, e);
-                    }
-                }
-            };
             if (user) {
                 if (requireAdmin && isRelease && process.env.PASSWORD) {
                     if (AdminPrivileges.get(user.id)) {
@@ -156,11 +146,29 @@ export default class RouteManager {
                         return;
                     }
                 }
-                await tryExecute(secureHandler, { ...core, user });
+                try {
+                    await secureHandler({ ...core, user });
+                } catch (e) {
+                    console.log(red(target), user && 'email' in user ? '<user logged out>' : undefined);
+                    if (errorHandler) {
+                        errorHandler({ ...core, error: e });
+                    } else {
+                        _error(res, `The server encountered an internal error when serving ${target}.`, e);
+                    }
+                }
             }
             // req.session!.target = target;
             else if (publicHandler) {
-                await tryExecute(publicHandler, core);
+                try {
+                    await publicHandler(core);
+                } catch (e) {
+                    console.log(red(target), user && 'email' in user ? '<user logged out>' : undefined);
+                    if (errorHandler) {
+                        errorHandler({ ...core, error: e });
+                    } else {
+                        _error(res, `The server encountered an internal error when serving ${target}.`, e);
+                    }
+                }
                 if (!res.headersSent) {
                     // res.redirect("/login");
                 }
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 0cc1553c0..3c7858a72 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -26,21 +26,12 @@ export const getSignup = (req: Request, res: Response) => {
     return undefined;
 };
 
-const tryRedirectToTarget = (req: Request, res: Response) => {
-    const target = (req.session as any)?.target;
-    if (req.session && target) {
-        res.redirect(target);
-    } else {
-        res.redirect('/home');
-    }
-};
-
 /**
  * POST /signup
  * Create a new local account.
  */
 export const postSignup = (req: Request, res: Response, next: NextFunction) => {
-    const email = req.body.email as String;
+    const email = req.body.email as string;
     check('email', 'Email is not valid').isEmail().run(req);
     check('password', 'Password must be at least 4 characters long').isLength({ min: 4 }).run(req);
     check('confirmPassword', 'Passwords do not match').equals(req.body.password).run(req);
@@ -66,7 +57,7 @@ export const postSignup = (req: Request, res: Response, next: NextFunction) => {
     const user = new User(model);
 
     User.findOne({ email })
-        .then((existingUser: any) => {
+        .then((existingUser: DashUserModel | null) => {
             if (existingUser) {
                 return res.redirect('/login');
             }
@@ -74,13 +65,15 @@ export const postSignup = (req: Request, res: Response, next: NextFunction) => {
                 .then(() => {
                     req.logIn(user, err => {
                         if (err) return next(err);
-                        tryRedirectToTarget(req, res);
+                        res.redirect('/home');
                         return undefined;
                     });
                 })
+                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                 .catch((err: any) => next(err));
             return undefined;
         })
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
         .catch((err: any) => next(err));
     return undefined;
 };
@@ -108,7 +101,8 @@ export const getLogin = (req: Request, res: Response) => {
 export const postLogin = (req: Request, res: Response, next: NextFunction) => {
     if (req.body.email === '') {
         User.findOne({ email: 'guest' })
-            .then((user: any) => !user && initializeGuest())
+            .then((user: DashUserModel | null) => !user && initializeGuest())
+            // eslint-disable-next-line @typescript-eslint/no-explicit-any
             .catch((err: any) => err);
         req.body.email = 'guest';
         req.body.password = 'guest';
@@ -132,7 +126,7 @@ export const postLogin = (req: Request, res: Response, next: NextFunction) => {
             req.logIn(user, loginErr => {
                 if (loginErr) {
                     next(loginErr);
-                } else tryRedirectToTarget(req, res);
+                } else res.redirect('/home');
             });
         return undefined;
     };
@@ -163,15 +157,15 @@ export const postForgot = function (req: Request, res: Response, next: NextFunct
     const { email } = req.body;
     async.waterfall(
         [
-            function (done: any) {
-                c.randomBytes(20, (err: any, buffer: Buffer) => {
+            function (done: (arg: null, token?: string) => void) {
+                c.randomBytes(20, (err: Error | null, buffer: Buffer) => {
                     if (err) {
                         done(null);
                     } else done(null, buffer.toString('hex'));
                 });
             },
-            function (token: string, done: any) {
-                User.findOne({ email }).then((user: any) => {
+            function (token: string, done: (arg: null, token: string, user: DashUserModel) => void) {
+                User.findOne({ email }).then((user: DashUserModel | null) => {
                     if (!user) {
                         // NO ACCOUNT WITH SUBMITTED EMAIL
                         res.redirect('/forgotPassword');
@@ -182,7 +176,7 @@ export const postForgot = function (req: Request, res: Response, next: NextFunct
                     user.save().then(() => done(null, token, user));
                 });
             },
-            function (token: Uint16Array, user: DashUserModel, done: any) {
+            function (token: Uint16Array, user: DashUserModel, done: (arg: null, token: Error | null, data: string) => void) {
                 const smtpTransport = nodemailer.createTransport({
                     service: 'Gmail',
                     auth: {
@@ -220,7 +214,7 @@ export const postForgot = function (req: Request, res: Response, next: NextFunct
 
 export const getReset = function (req: Request, res: Response) {
     User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } })
-        .then((user: any) => {
+        .then((user: DashUserModel | null) => {
             if (!user) return res.redirect('/forgotPassword');
             res.render('reset.pug', {
                 title: 'Reset Password',
@@ -234,9 +228,9 @@ export const getReset = function (req: Request, res: Response) {
 export const postReset = function (req: Request, res: Response) {
     async.waterfall(
         [
-            function (done: any) {
+            function (done: (args: null, user: DashUserModel) => void) {
                 User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } })
-                    .then((user: any) => {
+                    .then((user: DashUserModel | null) => {
                         if (!user) return res.redirect('back');
 
                         check('password', 'Password must be at least 4 characters long').isLength({ min: 4 }).run(req);
@@ -250,8 +244,8 @@ export const postReset = function (req: Request, res: Response) {
 
                         user.save()
                             .then(
-                                () => (req as any).logIn(user),
-                                (err: any) => err
+                                // eslint-disable-next-line @typescript-eslint/no-explicit-any
+                                () => req.logIn(user, (err: any) => err)
                             )
                             .catch(() => res.redirect('/login'));
                         done(null, user);
@@ -259,7 +253,7 @@ export const postReset = function (req: Request, res: Response) {
                     })
                     .catch(() => res.redirect('back'));
             },
-            function (user: DashUserModel, done: any) {
+            function (user: DashUserModel, done: (args: null, error: Error | null) => void) {
                 const smtpTransport = nodemailer.createTransport({
                     service: 'Gmail',
                     auth: {
diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts
index 6fd8dd593..7aa7f2598 100644
--- a/src/server/authentication/DashUserModel.ts
+++ b/src/server/authentication/DashUserModel.ts
@@ -3,30 +3,31 @@ import * as mongoose from 'mongoose';
 import { Utils } from '../../Utils';
 
 type comparePasswordFunction = (candidatePassword: string, cb: (err: Error, isMatch: boolean) => void) => void;
-type mongooseDocument = { id: string }; // & mongoose.Document;
-export type DashUserModel = mongooseDocument & {
-    email: string;
-    password: string;
-    passwordResetToken?: string;
-    passwordResetExpires?: Date;
+export type DashUserModel = mongoose.Document & {
+    email?: string | null | undefined;
+    password?: string | null | undefined;
+    passwordResetToken?: string | null | undefined;
+    passwordResetExpires?: Date | null | undefined;
 
-    dropboxRefresh?: string;
-    dropboxToken?: string;
+    dropboxRefresh?: string | null | undefined;
+    dropboxToken?: string | null | undefined;
 
-    userDocumentId: string;
-    sharingDocumentId: string;
-    linkDatabaseId: string;
-    cacheDocumentIds: string;
+    userDocumentId?: string | null | undefined;
+    sharingDocumentId?: string | null | undefined;
+    linkDatabaseId?: string | null | undefined;
+    cacheDocumentIds?: string | null | undefined;
+    profile?:
+        | {
+              name?: string | null | undefined;
+              gender?: string | null | undefined;
+              location?: string | null | undefined;
+              website?: string | null | undefined;
+              picture?: string | null | undefined;
+          }
+        | null
+        | undefined;
 
-    profile: {
-        name: string;
-        gender: string;
-        location: string;
-        website: string;
-        picture: string;
-    };
-
-    comparePassword: comparePasswordFunction;
+    comparePassword?: comparePasswordFunction | null | undefined;
 };
 
 export type AuthToken = {
@@ -68,36 +69,38 @@ const userSchema = new mongoose.Schema(
  */
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 userSchema.pre('save', function save(next: any) {
-    const user = this;
-    if (!user.isModified('password')) {
+    if (!this.isModified('password')) {
         return next();
     }
-    bcrypt.genSalt(10, (err: Error, salt: string) => {
-        if (err) {
-            return next(err);
-        }
-        bcrypt.hash(
-            user.password ?? '',
-            salt,
-            () => {},
-            (cryptErr: mongoose.Error, hash: string) => {
-                if (cryptErr) {
-                    return next(cryptErr);
-                }
-                user.password = hash;
-                next();
-                return undefined;
+    bcrypt.genSalt(
+        10,
+        ((err: Error, salt: string) => {
+            if (err) {
+                return next(err);
             }
-        );
-        return undefined;
-    });
+            bcrypt.hash(
+                this.password ?? '',
+                salt,
+                () => {},
+                (cryptErr: mongoose.Error, hash: string) => {
+                    if (cryptErr) {
+                        return next(cryptErr);
+                    }
+                    this.password = hash;
+                    next();
+                    return undefined;
+                }
+            );
+            return undefined;
+        }).bind(this)
+    );
     return undefined;
 });
 
 const comparePassword: comparePasswordFunction = function (this: DashUserModel, candidatePassword, cb) {
     // Choose one of the following bodies for authentication logic.
     // secure (expected, default)
-    bcrypt.compare(candidatePassword, this.password, cb);
+    this.password && bcrypt.compare(candidatePassword, this.password, cb);
     // bypass password (debugging)
     // cb(undefined, true);
 };
diff --git a/src/server/authentication/Passport.ts b/src/server/authentication/Passport.ts
index a62d38e3e..38a99bd45 100644
--- a/src/server/authentication/Passport.ts
+++ b/src/server/authentication/Passport.ts
@@ -1,25 +1,28 @@
 import * as passport from 'passport';
 import * as passportLocal from 'passport-local';
 import User, { DashUserModel } from './DashUserModel';
+import { IncomingMessage } from 'webpack-dev-middleware';
 
 const LocalStrategy = passportLocal.Strategy;
 
-passport.serializeUser<any, any>((req, user, done) => {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+passport.serializeUser<any, IncomingMessage & DashUserModel>((req, user, done) => {
     done(undefined, (user as DashUserModel)?.id);
 });
 
-passport.deserializeUser<any, any>((id, done) => {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+passport.deserializeUser<any, IncomingMessage & DashUserModel>((id, done) => {
     User.findById(id)
         .exec()
-        .then((user: DashUserModel) => done(undefined, user));
+        .then((user: DashUserModel | null) => user && done(undefined, user));
 });
 
 // AUTHENTICATE JUST WITH EMAIL AND PASSWORD
 passport.use(
     new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => {
         User.findOne({ email: email.toLowerCase() })
-            .then((user: DashUserModel) => {
-                if (!user) {
+            .then((user: DashUserModel | null) => {
+                if (!user?.comparePassword) {
                     done(undefined, false, { message: 'Invalid email or password' }); // invalid email
                 } else {
                     user.comparePassword(password, (error: Error, isMatch: boolean) => {
-- 
cgit v1.2.3-70-g09d2

