diff --git a/lib/api/archiveHandler.ts b/lib/api/archiveHandler.ts index c5e5f09..b158d97 100644 --- a/lib/api/archiveHandler.ts +++ b/lib/api/archiveHandler.ts @@ -86,17 +86,18 @@ export default async function archiveHandler(link: LinksAndCollectionAndOwner) { image: user.archiveAsScreenshot && !link.image?.startsWith("archive") ? "pending" - : "unavailable", + : undefined, pdf: user.archiveAsPDF && !link.pdf?.startsWith("archive") ? "pending" - : "unavailable", + : undefined, + monolith: + user.archiveAsMonolith && !link.monolith?.startsWith("archive") + ? "pending" + : undefined, readable: !link.readable?.startsWith("archive") ? "pending" : undefined, - monolith: !link.monolith?.startsWith("archive") - ? "pending" - : undefined, preview: !link.readable?.startsWith("archive") ? "pending" : undefined, diff --git a/package.json b/package.json index db8cf5d..1c428cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "linkwarden", - "version": "v2.6.0", + "version": "v2.6.2", "main": "index.js", "repository": "https://github.com/linkwarden/linkwarden.git", "author": "Daniel31X13 ", diff --git a/pages/api/v1/auth/[...nextauth].ts b/pages/api/v1/auth/[...nextauth].ts index dd38aa7..17bcaad 100644 --- a/pages/api/v1/auth/[...nextauth].ts +++ b/pages/api/v1/auth/[...nextauth].ts @@ -79,10 +79,7 @@ const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY; const providers: Provider[] = []; -if ( - process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === "true" || - process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === undefined -) { +if (process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED !== "false") { // undefined is for backwards compatibility providers.push( CredentialsProvider({ diff --git a/pages/api/v1/logins/index.ts b/pages/api/v1/logins/index.ts index 7e290af..8a98210 100644 --- a/pages/api/v1/logins/index.ts +++ b/pages/api/v1/logins/index.ts @@ -414,8 +414,7 @@ export function getLogins() { } return { credentialsEnabled: - process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === "true" || - process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED === undefined + process.env.NEXT_PUBLIC_CREDENTIALS_ENABLED !== "false" ? "true" : "false", emailEnabled: diff --git a/scripts/migration/descriptionToName.js b/scripts/migration/descriptionToName.js deleted file mode 100644 index 435e59f..0000000 --- a/scripts/migration/descriptionToName.js +++ /dev/null @@ -1,70 +0,0 @@ -// [Optional, but recommended] - -// We decided that the "name" field should be the auto-generated field instead of the "description" field, so we need to -// move the data from the "description" field to the "name" field for links that have an empty name. - -// This script is meant to be run only once. - -// Run the script with `node scripts/migration/descriptionToName.js` - -const { PrismaClient } = require("@prisma/client"); - -const prisma = new PrismaClient(); - -async function main() { - console.log("Starting..."); - - const count = await prisma.link.count({ - where: { - name: "", - description: { - not: "", - }, - }, - }); - - console.log( - `Applying the changes to ${count} ${ - count == 1 ? "link" : "links" - } in 10 seconds...` - ); - - await new Promise((resolve) => setTimeout(resolve, 10000)); - - console.log("Applying the changes..."); - - const links = await prisma.link.findMany({ - where: { - name: "", - description: { - not: "", - }, - }, - select: { - id: true, - description: true, - }, - }); - - for (const link of links) { - await prisma.link.update({ - where: { - id: link.id, - }, - data: { - name: link.description, - description: "", - }, - }); - } - - console.log("Done!"); -} - -main() - .catch((e) => { - throw e; - }) - .finally(async () => { - await prisma.$disconnect(); - }); diff --git a/scripts/migration/v2.6.1/index.js b/scripts/migration/v2.6.1/index.js new file mode 100644 index 0000000..0c537e0 --- /dev/null +++ b/scripts/migration/v2.6.1/index.js @@ -0,0 +1,169 @@ +// Run the script with `node scripts/migration/v2.6.1/index.js` +// Docker users can run the script with `docker exec -it CONTAINER_ID /bin/bash -c 'node scripts/migration/v2.6.1/index.js'` + +// There are two parts to this script: + +// Firstly we decided that the "name" field should be the auto-generated field instead of the "description" field, so we need to +// move the data from the "description" field to the "name" field for links that have an empty name. + +// Secondly it looks for every link and checks if the pdf/screenshot exist in the filesystem. +// If they do, it updates the link with the path in the db. +// If they don't, it passes. + +const { S3 } = require("@aws-sdk/client-s3"); +const { PrismaClient } = require("@prisma/client"); +const { existsSync } = require("fs"); +const util = require("util"); + +const prisma = new PrismaClient(); + +const STORAGE_FOLDER = process.env.STORAGE_FOLDER || "data"; + +const s3Client = + process.env.SPACES_ENDPOINT && + process.env.SPACES_REGION && + process.env.SPACES_KEY && + process.env.SPACES_SECRET + ? new S3({ + forcePathStyle: false, + endpoint: process.env.SPACES_ENDPOINT, + region: process.env.SPACES_REGION, + credentials: { + accessKeyId: process.env.SPACES_KEY, + secretAccessKey: process.env.SPACES_SECRET, + }, + }) + : undefined; + +async function checkFileExistence(path) { + if (s3Client) { + // One millisecond delay to avoid rate limiting + await new Promise((resolve) => setTimeout(resolve, 1)); + + const bucketParams = { + Bucket: process.env.SPACES_BUCKET_NAME, + Key: path, + }; + + try { + const headObjectAsync = util.promisify( + s3Client.headObject.bind(s3Client) + ); + + try { + await headObjectAsync(bucketParams); + return true; + } catch (err) { + return false; + } + } catch (err) { + console.log("Error:", err); + + return false; + } + } else { + try { + if (existsSync(STORAGE_FOLDER + "/" + path)) { + return true; + } else return false; + } catch (err) { + console.log(err); + } + } +} + +async function main() { + console.log("Starting... Please do not interrupt the process."); + + const linksWithoutName = await prisma.link.findMany({ + where: { + name: "", + description: { + not: "", + }, + }, + select: { + id: true, + description: true, + }, + }); + + for (const link of linksWithoutName) { + await prisma.link.update({ + where: { + id: link.id, + }, + data: { + name: link.description, + description: "", + }, + }); + } + + const links = await prisma.link.findMany({ + select: { + id: true, + collectionId: true, + image: true, + pdf: true, + readable: true, + monolith: true, + }, + orderBy: { id: "asc" }, + }); + + // PDFs + for (let link of links) { + const path = `archives/${link.collectionId}/${link.id}.pdf`; + + const res = await checkFileExistence(path); + + if (res) { + await prisma.link.update({ + where: { id: link.id }, + data: { pdf: path }, + }); + } + + console.log("Indexing the PDF for link:", link.id); + } + + // Screenshots (PNGs) + for (let link of links) { + const path = `archives/${link.collectionId}/${link.id}.png`; + + const res = await checkFileExistence(path); + + if (res) { + await prisma.link.update({ + where: { id: link.id }, + data: { image: path }, + }); + } + + console.log("Indexing the PNG for link:", link.id); + } + + // Screenshots (JPEGs) + for (let link of links) { + const path = `archives/${link.collectionId}/${link.id}.jpeg`; + + const res = await checkFileExistence(path); + + if (res) { + await prisma.link.update({ + where: { id: link.id }, + data: { image: path }, + }); + } + + console.log("Indexing the JPEG for link:", link.id); + } + + await prisma.$disconnect(); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +});