diff --git a/app/components/HoverPreviewDocument.tsx b/app/components/HoverPreviewDocument.tsx index 7786f32a6..0edaa96dc 100644 --- a/app/components/HoverPreviewDocument.tsx +++ b/app/components/HoverPreviewDocument.tsx @@ -9,7 +9,7 @@ import useStores from "~/hooks/useStores"; type Props = { url: string; - children: (arg0: React.ReactNode) => React.ReactNode; + children: (content: React.ReactNode) => React.ReactNode; }; function HoverPreviewDocument({ url, children }: Props) { @@ -23,20 +23,27 @@ function HoverPreviewDocument({ url, children }: Props) { const document = slug ? documents.getByUrl(slug) : undefined; if (!document) return null; - return children( - - {document.titleWithDefault} - + return ( + <> + {children( + + {document.titleWithDefault} + - }> - - - + }> + + + + )} + ); } @@ -49,5 +56,4 @@ const Heading = styled.h2` color: ${(props) => props.theme.text}; `; -// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '({ url, children }: Props) => Re... Remove this comment to see the full error message export default observer(HoverPreviewDocument); diff --git a/app/scenes/Document/components/Document.tsx b/app/scenes/Document/components/Document.tsx index 6810fd792..c751ae87c 100644 --- a/app/scenes/Document/components/Document.tsx +++ b/app/scenes/Document/components/Document.tsx @@ -497,8 +497,6 @@ class DocumentScene extends React.Component { } savingIsDisabled={document.isSaving || this.isEmpty} sharedTree={this.props.sharedTree} - // @ts-expect-error ts-migrate(2322) FIXME: Type '{ document: Document; shareId: any; isRevisi... Remove this comment to see the full error message - goBack={this.goBack} onSelectTemplate={this.replaceDocument} onSave={this.onSave} headings={headings} diff --git a/app/scenes/Document/components/Editor.tsx b/app/scenes/Document/components/Editor.tsx index c398fd0b8..dc1a04d8c 100644 --- a/app/scenes/Document/components/Editor.tsx +++ b/app/scenes/Document/components/Editor.tsx @@ -124,7 +124,7 @@ class DocumentEditor extends React.Component { {...rest} /> {!readOnly && } - {this.activeLinkEvent && !shareId && readOnly && ( + {this.activeLinkEvent && !shareId && ( void; - onDiscard: () => void; onSave: (options: { done?: boolean; publish?: boolean; diff --git a/server/commands/collectionExporter.ts b/server/commands/collectionExporter.ts index fa12f29e5..6e3864462 100644 --- a/server/commands/collectionExporter.ts +++ b/server/commands/collectionExporter.ts @@ -27,6 +27,7 @@ export default async function collectionExporter({ userId: user.id, teamId: user.teamId, }); + // Event is consumed on worker in queues/processors/exports await Event.create({ name: collection ? "collections.export" : "collections.export_all", @@ -36,6 +37,7 @@ export default async function collectionExporter({ modelId: fileOperation.id, ip, }); + fileOperation.user = user; fileOperation.collection = collection; return fileOperation; diff --git a/server/commands/documentMover.test.ts b/server/commands/documentMover.test.ts index c91bc360b..4c9954419 100644 --- a/server/commands/documentMover.test.ts +++ b/server/commands/documentMover.test.ts @@ -134,6 +134,7 @@ describe("documentMover", () => { title: "Child document", text: `content ![attachment](${attachment.redirectUrl})`, }); + await collection.addDocumentToStructure(newDocument); await documentMover({ user, @@ -143,9 +144,11 @@ describe("documentMover", () => { index: 0, ip, }); + // check document ids where updated await newDocument.reload(); expect(newDocument.collectionId).toBe(newCollection.id); + // check new attachment was created pointint to same key const attachmentIds = parseAttachmentIds(newDocument.text); const newAttachment = await Attachment.findByPk(attachmentIds[0]); diff --git a/server/commands/documentMover.ts b/server/commands/documentMover.ts index 67311f95b..5301d568f 100644 --- a/server/commands/documentMover.ts +++ b/server/commands/documentMover.ts @@ -1,9 +1,12 @@ +import { Transaction } from "sequelize"; import { Document, Attachment, Collection, User, Event } from "@server/models"; import parseAttachmentIds from "@server/utils/parseAttachmentIds"; import { sequelize } from "../sequelize"; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -async function copyAttachments(document: Document, options) { +async function copyAttachments( + document: Document, + options?: { transaction?: Transaction } +) { // @ts-expect-error ts-migrate(2339) FIXME: Property 'text' does not exist on type 'Document'. let text = document.text; // @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'Document'. @@ -41,7 +44,6 @@ export default async function documentMover({ user, document, collectionId, - // @ts-expect-error ts-migrate(2322) FIXME: Type 'null' is not assignable to type 'string'. parentDocumentId = null, // convert undefined to null so parentId comparison treats them as equal index, @@ -51,12 +53,11 @@ export default async function documentMover({ user: User; document: Document; collectionId: string; - parentDocumentId?: string; + parentDocumentId?: string | null; index?: number; ip: string; }) { - // @ts-expect-error ts-migrate(7034) FIXME: Variable 'transaction' implicitly has type 'any' i... Remove this comment to see the full error message - let transaction; + let transaction: Transaction | undefined; // @ts-expect-error ts-migrate(2339) FIXME: Property 'collectionId' does not exist on type 'Do... Remove this comment to see the full error message const collectionChanged = collectionId !== document.collectionId; const result = { @@ -86,6 +87,7 @@ export default async function documentMover({ } else { try { transaction = await sequelize.transaction(); + // remove from original collection // @ts-expect-error ts-migrate(2339) FIXME: Property 'collectionId' does not exist on type 'Do... Remove this comment to see the full error message const collection = await Collection.findByPk(document.collectionId, { @@ -98,6 +100,7 @@ export default async function documentMover({ ] = (await collection.removeDocumentInStructure(document, { save: false, })) || [undefined, index]; + // if we're reordering from within the same parent // the original and destination collection are the same, // so when the initial item is removed above, the list will reduce by 1. @@ -166,7 +169,6 @@ export default async function documentMover({ childDocuments.map(async (child) => { await loopChildren(child.id); child.text = await copyAttachments(child, { - // @ts-expect-error ts-migrate(7005) FIXME: Variable 'transaction' implicitly has an 'any' typ... Remove this comment to see the full error message transaction, }); child.collectionId = collectionId; @@ -190,7 +192,10 @@ export default async function documentMover({ document.collection = newCollection; // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'Document' is not assignable to p... Remove this comment to see the full error message result.documents.push(document); - await transaction.commit(); + + if (transaction) { + await transaction.commit(); + } } catch (err) { if (transaction) { await transaction.rollback(); diff --git a/server/utils/parseAttachmentIds.ts b/server/utils/parseAttachmentIds.ts index cce40c788..d69be094c 100644 --- a/server/utils/parseAttachmentIds.ts +++ b/server/utils/parseAttachmentIds.ts @@ -1,7 +1,11 @@ +import { compact } from "lodash"; + const attachmentRegex = /\/api\/attachments\.redirect\?id=(?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi; -export default function parseAttachmentIds(text: any): string[] { - return [...text.matchAll(attachmentRegex)].map( - (match) => match.groups && match.groups.id +export default function parseAttachmentIds(text: string): string[] { + return compact( + [...text.matchAll(attachmentRegex)].map( + (match) => match.groups && match.groups.id + ) ); } diff --git a/server/utils/passport.ts b/server/utils/passport.ts index 9e89d1c0d..b1ae3d919 100644 --- a/server/utils/passport.ts +++ b/server/utils/passport.ts @@ -1,51 +1,52 @@ import crypto from "crypto"; import { addMinutes, subMinutes } from "date-fns"; import fetch from "fetch-with-proxy"; -import { Request } from "koa"; +import { Context } from "koa"; import { OAuthStateMismatchError } from "../errors"; import { getCookieDomain } from "./domains"; export class StateStore { key = "state"; - store = (req: Request, callback: () => void) => { + store = ( + ctx: Context, + callback: (err: Error | null, state: string) => void + ) => { // Produce a random string as state const state = crypto.randomBytes(8).toString("hex"); - // @ts-expect-error ts-migrate(2339) FIXME: Property 'cookies' does not exist on type 'Request... Remove this comment to see the full error message - req.cookies.set(this.key, state, { + ctx.cookies.set(this.key, state, { httpOnly: false, expires: addMinutes(new Date(), 10), - domain: getCookieDomain(req.hostname), + domain: getCookieDomain(ctx.hostname), }); - // @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 2. + callback(null, state); }; - verify = (req: Request, providedState: string, callback: () => void) => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'cookies' does not exist on type 'Request... Remove this comment to see the full error message - const state = req.cookies.get(this.key); + verify = ( + ctx: Context, + providedState: string, + callback: (err: Error | null, success?: boolean) => void + ) => { + const state = ctx.cookies.get(this.key); if (!state) { return callback( - // @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 1. - new OAuthStateMismatchError("State not return in OAuth flow") + OAuthStateMismatchError("State not return in OAuth flow") ); } - // @ts-expect-error ts-migrate(2339) FIXME: Property 'cookies' does not exist on type 'Request... Remove this comment to see the full error message - req.cookies.set(this.key, "", { + ctx.cookies.set(this.key, "", { httpOnly: false, expires: subMinutes(new Date(), 1), - domain: getCookieDomain(req.hostname), + domain: getCookieDomain(ctx.hostname), }); if (state !== providedState) { - // @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 1. - return callback(new OAuthStateMismatchError()); + return callback(OAuthStateMismatchError()); } - // @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 2. callback(null, true); }; } diff --git a/server/utils/removeIndexCollision.ts b/server/utils/removeIndexCollision.ts index bd1915c8d..a0025ac5f 100644 --- a/server/utils/removeIndexCollision.ts +++ b/server/utils/removeIndexCollision.ts @@ -1,13 +1,13 @@ import fractionalIndex from "fractional-index"; import { Collection } from "@server/models"; import { sequelize, Op } from "../sequelize"; + /** * * @param teamId The team id whose collections has to be fetched * @param index the index for which collision has to be checked * @returns An index, if there is collision returns a new index otherwise the same index */ - export default async function removeIndexCollision( teamId: string, index: string diff --git a/server/utils/s3.ts b/server/utils/s3.ts index d51a006e3..d2e109dd0 100644 --- a/server/utils/s3.ts +++ b/server/utils/s3.ts @@ -7,7 +7,7 @@ import Logger from "@server/logging/logger"; const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY; const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID; -const AWS_REGION = process.env.AWS_REGION; +const AWS_REGION = process.env.AWS_REGION || ""; const AWS_S3_UPLOAD_BUCKET_NAME = process.env.AWS_S3_UPLOAD_BUCKET_NAME || ""; const AWS_S3_FORCE_PATH_STYLE = process.env.AWS_S3_FORCE_PATH_STYLE !== "false"; const s3 = new AWS.S3({ @@ -25,11 +25,13 @@ const s3 = new AWS.S3({ signatureVersion: "v4", }); -const hmac = (key: string, message: string, encoding: any) => { - return crypto - .createHmac("sha256", key) - .update(message, "utf8") - .digest(encoding); +const hmac = ( + key: string | Buffer, + message: string, + encoding?: "base64" | "hex" +) => { + const o = crypto.createHmac("sha256", key).update(message, "utf8"); + return encoding ? o.digest(encoding) : o.digest(); }; export const makeCredential = () => { @@ -75,20 +77,17 @@ export const makePolicy = ( ], expiration: format(tomorrow, "yyyy-MM-dd'T'HH:mm:ss'Z'"), }; + return Buffer.from(JSON.stringify(policy)).toString("base64"); }; -export const getSignature = (policy: any) => { - // @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2. +export const getSignature = (policy: string) => { const kDate = hmac( "AWS4" + AWS_SECRET_ACCESS_KEY, format(new Date(), "yyyyMMdd") ); - // @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2. const kRegion = hmac(kDate, AWS_REGION); - // @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2. const kService = hmac(kRegion, "s3"); - // @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2. const kCredentials = hmac(kService, "aws4_request"); const signature = hmac(kCredentials, policy, "hex"); return signature; @@ -198,7 +197,6 @@ export const getAWSKeyForFileOp = (teamId: string, name: string) => { return `${bucket}/${teamId}/${uuidv4()}/${name}-export.zip`; }; -// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value. export const getFileByKey = async (key: string) => { const params = { Bucket: AWS_S3_UPLOAD_BUCKET_NAME, @@ -207,10 +205,12 @@ export const getFileByKey = async (key: string) => { try { const data = await s3.getObject(params).promise(); - return data.Body; + return data.Body || null; } catch (err) { Logger.error("Error getting file from S3 by key", err, { key, }); } + + return null; }; diff --git a/server/utils/zip.ts b/server/utils/zip.ts index 52e84de55..ad93cd029 100644 --- a/server/utils/zip.ts +++ b/server/utils/zip.ts @@ -3,11 +3,11 @@ import JSZip from "jszip"; import tmp from "tmp"; import Logger from "@server/logging/logger"; import { Attachment, Collection, Document } from "@server/models"; +import { NavigationNode } from "~/types"; import { serializeFilename } from "./fs"; import { getFileByKey } from "./s3"; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'zip' implicitly has an 'any' type. -async function addToArchive(zip, documents) { +async function addToArchive(zip: JSZip, documents: NavigationNode[]) { for (const doc of documents) { const document = await Document.findByPk(doc.id); @@ -39,15 +39,19 @@ async function addToArchive(zip, documents) { if (doc.children && doc.children.length) { const folder = zip.folder(title); - await addToArchive(folder, doc.children); + + if (folder) { + await addToArchive(folder, doc.children); + } } } } -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'zip' implicitly has an 'any' type. -async function addImageToArchive(zip, key) { +async function addImageToArchive(zip: JSZip, key: string) { try { const img = await getFileByKey(key); + + // @ts-expect-error Blob zip.file(key, img, { createFolders: true, }); @@ -58,8 +62,7 @@ async function addImageToArchive(zip, key) { } } -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'zip' implicitly has an 'any' type. -async function archiveToPath(zip) { +async function archiveToPath(zip: JSZip) { return new Promise((resolve, reject) => { tmp.file( { @@ -88,7 +91,10 @@ export async function archiveCollections(collections: Collection[]) { for (const collection of collections) { if (collection.documentStructure) { const folder = zip.folder(collection.name); - await addToArchive(folder, collection.documentStructure); + + if (folder) { + await addToArchive(folder, collection.documentStructure); + } } } diff --git a/server/validation.ts b/server/validation.ts index 5dffa124a..4243a1f48 100644 --- a/server/validation.ts +++ b/server/validation.ts @@ -4,21 +4,23 @@ import { validateColorHex } from "../shared/utils/color"; import { validateIndexCharacters } from "../shared/utils/indexCharacters"; import { ParamRequiredError, ValidationError } from "./errors"; -export const assertPresent = (value: any, message: string) => { +export const assertPresent = (value: unknown, message: string) => { if (value === undefined || value === null || value === "") { throw ParamRequiredError(message); } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -export const assertArray = (value, message) => { +export const assertArray = (value: unknown, message?: string) => { if (!isArrayLike(value)) { throw ValidationError(message); } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -export const assertIn = (value, options, message) => { +export const assertIn = ( + value: string, + options: (string | null)[], + message?: string +) => { if (!options.includes(value)) { throw ValidationError(message); } @@ -34,22 +36,19 @@ export const assertSort = ( } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -export const assertNotEmpty = (value, message) => { +export const assertNotEmpty = (value: unknown, message?: string) => { if (value === "") { throw ValidationError(message); } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'message' implicitly has an 'any' type. -export const assertEmail = (value = "", message) => { +export const assertEmail = (value = "", message?: string) => { if (!validator.isEmail(value)) { throw ValidationError(message); } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'message' implicitly has an 'any' type. -export const assertUuid = (value, message) => { +export const assertUuid = (value: unknown, message?: string) => { if (typeof value !== "string") { throw ValidationError(message); } @@ -58,8 +57,7 @@ export const assertUuid = (value, message) => { } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -export const assertPositiveInteger = (value, message) => { +export const assertPositiveInteger = (value: unknown, message?: string) => { if ( !validator.isInt(String(value), { min: 0, @@ -69,22 +67,23 @@ export const assertPositiveInteger = (value, message) => { } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -export const assertHexColor = (value, message) => { +export const assertHexColor = (value: string, message?: string) => { if (!validateColorHex(value)) { throw ValidationError(message); } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -export const assertValueInArray = (value, values, message) => { +export const assertValueInArray = ( + value: string, + values: string[], + message?: string +) => { if (!values.includes(value)) { throw ValidationError(message); } }; -// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -export const assertIndexCharacters = (value, message) => { +export const assertIndexCharacters = (value: string, message?: string) => { if (!validateIndexCharacters(value)) { throw ValidationError(message); }