+ + Reset your password? ++ ++ Hi {{user}}! + + ++ Someone has requested a link to change your password. + + +
+ If you didn't request this, you can safely ignore this email + and your password will not be changed. + + |
+
+
+ {t("you_have_no_collections")} +
+ ); } else return ({name}
-{value}
+{value}
+
- {unescapeString(link.name || link.description) || link.url} -
- - { - e.stopPropagation(); - }} - className="flex gap-1 item-center select-none text-neutral mt-1 hover:opacity-70 duration-100" - > - -{shortendURL}
- -+ {unescapeString(link.name)} +
+ +Description
++ {t("description")} +
-{link.description ? ( unescapeString(link.description) ) : ( - No description provided. + {t("no_description")} )}
- {link.tags[0] && ( + {link.tags && link.tags[0] && ( <> -Tags
++ {t("tags")} +
-{formattedDate}
-
{shortendURL}
- -{shortendURL}
+ + ) : ( +- {unescapeString(link.name || link.description) || link.url} -
- -{shortendURL}
-{unescapeString(link.description)}
- {link.tags[0] ? ( -- {unescapeString(link.name || link.description) || link.url} + {link.name ? ( + unescapeString(link.name) + ) : ( +
{shortendURL}
- - ) : ( -+ {unescapeString(link.name)} +
+ ++ {unescapeString(link.description)} +
+ )} + + {link.tags[0] && ( ++ {t("description")} +
+ ++ {link.description ? ( + unescapeString(link.description) + ) : ( + + {t("no_description")} + + )} +
+ {link.tags[0] && ( + <> ++ {t("tags")} +
+ +Loading...
-- Delete {selectedLinks.length} Link{selectedLinks.length > 1 ? "s" : ""} + {selectedLinks.length === 1 + ? t("delete_link") + : t("delete_links", { count: selectedLinks.length })}
Are you sure you want to delete {selectedLinks.length} links?
- ) : ( -Are you sure you want to delete this link?
- )} ++ {selectedLinks.length === 1 + ? t("link_deletion_confirmation_message") + : t("links_deletion_confirmation_message", { + count: selectedLinks.length, + })} +
- Hold the Shift key while clicking - 'Delete' to bypass this confirmation in the future. -
+{t("shift_key_tip")}
-- Edit {selectedLinks.length} Link{selectedLinks.length > 1 ? "s" : ""} + {selectedLinks.length === 1 + ? t("edit_link") + : t("edit_links", { count: selectedLinks.length })}
Move to Collection
+{t("move_to_collection")}
Add Tags
+{t("add_tags")}
- {permissions === true ? "Delete" : "Leave"} Collection + {permissions === true ? t("delete_collection") : t("leave_collection")}
@@ -68,48 +68,37 @@ export default function DeleteCollectionModal({- To confirm, type " - {collection.name} - " in the box below: -
- -{t("confirm_deletion_prompt", { name: collection.name })}
+Click the button below to leave the current collection.
+{t("leave_prompt")}
)} -Delete Link
+{t("delete_link")}
Are you sure you want to delete this Link?
+{t("link_deletion_confirmation_message")}
- Hold the Shift key while clicking - 'Delete' to bypass this confirmation in the future. -
+{t("shift_key_tip")}
-{t("delete_user")}
+ + + +{t("confirm_user_deletion")}
+ +Edit Collection Info
+{t("edit_collection_info")}
Name
+{t("name")}
Color
-{t("color")}
+Description
+{t("description")}
- {permissions === true ? "Share and Collaborate" : "Team"} + {permissions === true ? t("share_and_collaborate") : t("team")}
@@ -102,7 +105,7 @@ export default function EditCollectionSharingModal({Make Public
+{t("make_collection_public")}
- This will let Anyone to view this collection and it's - users. + {t("make_collection_public_desc")}
Sharable Link (Click to copy)
+{t("sharable_link_guide")}
Members
+{t("members")}
Owner
+{t("owner")}
Viewer
-Read-only access
++ {t("viewer")} +
+{t("viewer_desc")}
Contributor
-Can view and create Links
++ {t("contributor")} +
+{t("contributor_desc")}
Admin
-Full access to all Links
++ {t("admin")} +
+{t("admin_desc")}
Edit Link
+{t("edit_link")}
@@ -87,42 +80,31 @@ export default function EditLinkModal({ onClose, activeLink }: Props) { target="_blank" > -{shortendURL}
+{shortenedURL}
) : undefined}Name
+{t("name")}
Collection
+{t("collection")}
{link.collection.name ? (Tags
+{t("tags")}
Description
+{t("description")}
{t("confirm_password")}
+ + + ++ {t("password_change_warning")} + {process.env.NEXT_PUBLIC_STRIPE === "true" && t("stripe_update_note")} +
+ ++ {t("sso_will_be_removed_warning", { + service: + process.env.NEXT_PUBLIC_GOOGLE_ENABLED === "true" ? "Google" : "", + })} +
+ +{t("old_email")}
+{oldEmail}
+{t("new_email")}
+{newEmail}
+{t("password")}
+New Sub-Collection
-For {parent.name}
+{t("new_sub_collection")}
++ {t("for_collection", { name: parent.name })} +
> ) : ( -Create a New Collection
+{t("create_new_collection")}
)} @@ -72,19 +75,25 @@ export default function NewCollectionModal({ onClose, parent }: Props) {Name
-{t("name")}
+Color
-{t("color")}
+Description
+{t("description")}
Create a New Link
- +{t("create_new_link")}
-Link
+{t("link")}
Collection
+{t("collection")}
{link.collection.name ? (Name
+{t("name")}
Tags
+{t("tags")}
Description
+{t("description")}
- {optionsExpanded ? "Hide" : "More"} Options -
- +{optionsExpanded ? t("hide_options") : t("more_options")}
+Access Token Created
-- Your new token has been created. Please copy it and store it - somewhere safe. You will not be able to see it again. -
+{t("access_token_created")}
+{t("token_creation_notice")}
Create an Access Token
+{t("create_access_token")}
Name
+{t("name")}
Expires in
+{t("expires_in")}
{t("create_new_user")}
+ + + + +Preserved Formats
- +{t("preserved_formats")}
- {screenshotAvailable(link) || pdfAvailable(link) || readabilityAvailable(link) || singlefileAvailable(link) ? ( -- The following formats are available for this link: -
+{t("available_formats")}
) : ( "" )} @@ -167,7 +161,7 @@ export default function PreservedFormatsModal({ onClose, activeLink }: Props) {- Link preservation is in the queue -
-- Please check back later to see the result -
+{t("preservation_in_queue")}
+{t("check_back_later")}
There are more preserved formats in the queue
-- Please check back later to see the result -
+{t("check_back_later")}
- View latest snapshot on archive.org -
+{t("view_latest_snapshot")}
- {link?.collection.ownerId === session.data?.user.id ? ( -Refresh Preserved Formats
+{t("refresh_preserved_formats")}
- This deletes the current preservations + {t("this_deletes_current_preservations")}
Revoke Token
+{t("revoke_token")}
- Are you sure you want to revoke this Access Token? Any apps or - services using this token will no longer be able to access Linkwarden - using it. -
+{t("revoke_confirmation")}
-Upload File
+{t("upload_file")}
File
+{t("file")}
- PDF, PNG, JPG, HTML (Up to {process.env.NEXT_PUBLIC_MAX_FILE_SIZE || 30} - MB) + {t("file_types", { + size: process.env.NEXT_PUBLIC_MAX_FILE_SIZE || 30, + })}
Collection
+{t("collection")}
{link.collection.name ? (Name
+{t("name")}
Tags
+{t("tags")}
Description
+{t("description")}
{optionsExpanded ? "Hide" : "More"} Options
++ {optionsExpanded ? t("hide") : t("more")} {t("options")} +
{text || "You haven't created any Links Here"}
-- Start your journey by creating a new Link! -
+{t("start_journey")}
- The Link preservation is currently in the queue -
-- Please check back later to see the result + {t("link_preservation_in_queue")}
+{t("check_back_later")}
Account
+{t("account")}
Preference
+{t("preference")}
Access Tokens
+{t("access_tokens")}
Password
+{t("password")}
Billing
+{t("billing")}
Help
+{t("help")}
GitHub
+{t("github")}
{t("twitter")}
Mastodon
+{t("mastodon")}
Collections
+{t("collections")}
-Tags
+{t("tags")}
- You Have No Tags... + {t("you_have_no_tags")}
- By {collectionOwner.name} + +
{activeCollection.members.length > 0 && - ` and ${activeCollection.members.length} others`} - . + activeCollection.members.length === 1 + ? t("by_author_and_other", { + author: collectionOwner.name, + count: activeCollection.members.length, + }) + : activeCollection.members.length > 0 && + activeCollection.members.length !== 1 + ? t("by_author_and_others", { + author: collectionOwner.name, + count: activeCollection.members.length, + }) + : t("by_author", { + author: collectionOwner.name, + })}
Showing {activeCollection?._count?.links} results
-+ {activeCollection?._count?.links === 1 + ? t("showing_count_result", { + count: activeCollection?._count?.links, + }) + : t("showing_count_results", { + count: activeCollection?._count?.links, + })} +
+New Collection
++ {t("new_collection")} +
- Please check your Email + {t("check_your_email")}
-A sign in link has been sent to your email address.
+{t("verification_email_sent_desc")}
-- Didn't see the email? Check your spam folder or visit the{" "} - - Password Recovery - {" "} - page to resend the link. -
+- View Your Recently Added Links Here! + {t("view_added_links_here")}
- This section will view your latest added Links across every - Collections you have access to. + {t("view_added_links_here_desc")}
Import From
+{t("import_links")}
- Pin Your Favorite Links Here! + {t("pin_favorite_links_here")}
-- You can Pin your favorite Links by clicking on the three dots on - each Link and clicking{" "} - Pin to Dashboard. +
+ {t("pin_favorite_links_here_desc")}
This collection is empty...
+{t("collection_is_empty")}
)} {/*
@@ -255,3 +259,5 @@ export default function PublicCollections() {
<>>
);
}
+
+export { getServerSideProps };
diff --git a/pages/register.tsx b/pages/register.tsx
index 2f11488..4eba2bc 100644
--- a/pages/register.tsx
+++ b/pages/register.tsx
@@ -1,11 +1,18 @@
import Link from "next/link";
-import { useState, FormEvent } from "react";
+import React, { useState, FormEvent } from "react";
import { toast } from "react-hot-toast";
import { signIn } from "next-auth/react";
import { useRouter } from "next/router";
import CenteredForm from "@/layouts/CenteredForm";
import TextInput from "@/components/TextInput";
-import AccentSubmitButton from "@/components/AccentSubmitButton";
+import Button from "@/components/ui/Button";
+import { getLogins } from "./api/v1/logins";
+import { GetServerSideProps, InferGetServerSidePropsType } from "next";
+import { getToken } from "next-auth/jwt";
+import { prisma } from "@/lib/api/db";
+import { serverSideTranslations } from "next-i18next/serverSideTranslations";
+import { i18n } from "next-i18next.config";
+import { Trans, useTranslation } from "next-i18next";
const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER === "true";
@@ -17,7 +24,10 @@ type FormData = {
passwordConfirmation: string;
};
-export default function Register() {
+export default function Register({
+ availableLogins,
+}: InferGetServerSidePropsType
- Registration is disabled for this instance, please contact the admin
- in case of any issues.
- {t("registration_disabled")}
- Enter your details
+ {t("enter_details")}
Display Name
+ {t("display_name")}
+ Username
+ {t("username")}
+ Email {t("email")} Password
+ {t("password")}
+
- Confirm Password
+ {t("confirm_password")}
- By signing up, you agree to our{" "}
-
- Terms of Service
- {" "}
- and{" "}
-
- Privacy Policy
-
- .
-
- Need help?{" "}
-
- Get in touch
-
- .
+
+ Already have an account? {t("already_registered")} {t("need_help")}
- Nothing found.{" "}
-
- ¯\_(ツ)_/¯
-
- {t("nothing_found")} Access Tokens
+ {t("access_tokens")}
+
- Access Tokens can be used to access Linkwarden from other apps and
- services without giving away your Username and Password.
- {t("access_tokens_description")} Account Settings
+ {t("accountSettings")}
+ Display Name {t("display_name")} Username {t("username")} Email
- Updating this field will change your billing email as well
- {t("email")} {t("language")} Profile Photo {t("profile_photo")} {t("profile_privacy_info")} {t("whitelisted_users")}
+ {t("whitelisted_users_info")}
+
- Import & Export
+ {t("import_export")}
Import your data from other platforms. {t("import_data")} Import From Download your data instantly. {t("download_data")} Export Data {t("export_data")}
- Profile Visibility
-
- This will limit who can find and add you to new Collections.
- Whitelisted Users
- Please provide the Username of the users you wish to grant
- visibility to your profile. Separated by comma.
-
- Delete Account
+ {t("delete_account")}
- This will permanently delete ALL the Links, Collections, Tags, and
- archived data you own.{" "}
+ {t("delete_account_warning")}
{process.env.NEXT_PUBLIC_STRIPE
- ? "It will also cancel your subscription. "
- : undefined}{" "}
- You will be prompted to enter your password before the deletion
- process.
+ ? " " + t("cancel_subscription_notice")
+ : undefined}
Delete Your Account Billing Settings
+ {t("billing_settings")}
+
- To manage/cancel your subscription, visit the{" "}
+ {t("manage_subscription_intro")}{" "}
- Billing Portal
+ {t("billing_portal")}
.
- If you still need help or encountered any issues, feel free to reach
- out to us at:{" "}
-
+ {t("help_contact_intro")}{" "}
+
support@linkwarden.app
- Delete Account
+ {t("delete_account")}
- This will permanently delete all the Links, Collections, Tags, and
- archived data you own. It will also log you out
- {process.env.NEXT_PUBLIC_STRIPE
- ? " and cancel your subscription"
- : undefined}
- . This action is irreversible!
- {t("delete_warning")} Confirm Your Password {t("confirm_password")} Delete Your Account {t("delete_your_account")} Change Password
+ {t("change_password")}
+
- To change your password, please fill out the following. Your password
- should be at least 8 characters.
- {t("password_change_instructions")} New Password {t("old_password")} Confirm New Password {t("new_password")} Preference {t("preference")} Select Theme {t("select_theme")} Dark {t("dark")} Light {t("light")}
- Archive Settings
+ {t("archive_settings")}
Formats to Archive/Preserve webpages: {t("formats_to_archive")} Link Settings
+ {t("link_settings")}
+ Clicking on Links should: {t("clicking_on_links_should")}
- Subscribe to Linkwarden!
+ {t("subscribe_title")}
- You will be redirected to Stripe, feel free to reach out to us at{" "}
-
- support@linkwarden.app
- {" "}
- in case of any issue.
+ Monthly {t("monthly")} Yearly {t("yearly")}
- Billed {plan === Plan.monthly ? "Monthly" : "Yearly"}
+ {plan === Plan.monthly ? t("billed_monthly") : t("billed_yearly")}
+ Hi {{user}}!
+
+ Someone has requested a link to change your password.
+
+ If you didn't request this, you can safely ignore this email
+ and your password will not be changed.
+
+ Thank you for signing up with Linkwarden! 🥳
+
+ Just one small step left. Simply verify your email address,
+ and you’re all set!
+
+ If you’re having trouble clicking the button, click on the
+ following link:
+
+ Hi {{user}}!
+
+ You recently requested to change the email address
+ associated with your
+ Linkwarden account. To verify your
+ new email address, please click the button below.
+
+ Old Email:
+
+ {{oldEmail}}
+
+ New Email:
+
+ {{newEmail}}
+
- {/* head */}
@@ -105,3 +106,5 @@ export default function AccessTokens() {
);
}
+
+export { getServerSideProps };
diff --git a/pages/settings/account.tsx b/pages/settings/account.tsx
index 8d3134e..cb46442 100644
--- a/pages/settings/account.tsx
+++ b/pages/settings/account.tsx
@@ -12,14 +12,19 @@ import { MigrationFormat, MigrationRequest } from "@/types/global";
import Link from "next/link";
import Checkbox from "@/components/Checkbox";
import { dropdownTriggerer } from "@/lib/client/utils";
+import EmailChangeVerificationModal from "@/components/ModalContent/EmailChangeVerificationModal";
+import Button from "@/components/ui/Button";
+import { i18n } from "next-i18next.config";
+import { useTranslation } from "next-i18next";
+import getServerSideProps from "@/lib/client/getServerSideProps";
+
+const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
export default function Account() {
- const emailEnabled = process.env.NEXT_PUBLIC_EMAIL_PROVIDER;
-
+ const [emailChangeVerificationModal, setEmailChangeVerificationModal] =
+ useState(false);
const [submitLoader, setSubmitLoader] = useState(false);
-
const { account, updateAccount } = useAccountStore();
-
const [user, setUser] = useState
- Name
- Created
- Expires
+ {t("name")}
+ {t("created")}
+ {t("expires")}
+
*/}
+
*/}
+
+
+
+
diff --git a/templates/verifyEmail.html b/templates/verifyEmail.html
new file mode 100644
index 0000000..14e62ea
--- /dev/null
+++ b/templates/verifyEmail.html
@@ -0,0 +1,413 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Reset your password?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Change Password
+
+
+
+
+
+
+
+
diff --git a/templates/verifyEmailChange.html b/templates/verifyEmailChange.html
new file mode 100644
index 0000000..284b837
--- /dev/null
+++ b/templates/verifyEmailChange.html
@@ -0,0 +1,424 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Verify your email address
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Verify Email
+
+
+
+
+
+
+
+
+
+
diff --git a/types/enviornment.d.ts b/types/enviornment.d.ts
index 15e66fa..b228e22 100644
--- a/types/enviornment.d.ts
+++ b/types/enviornment.d.ts
@@ -13,6 +13,8 @@ declare global {
MAX_LINKS_PER_USER?: string;
ARCHIVE_TAKE_COUNT?: string;
IGNORE_UNAUTHORIZED_CA?: string;
+ IGNORE_URL_SIZE_LIMIT?: string;
+ ADMINISTRATOR?: string;
SPACES_KEY?: string;
SPACES_SECRET?: string;
@@ -28,13 +30,14 @@ declare global {
EMAIL_FROM?: string;
EMAIL_SERVER?: string;
+ BASE_URL?: string; // Used for email and stripe
+
NEXT_PUBLIC_STRIPE?: string;
STRIPE_SECRET_KEY?: string;
MONTHLY_PRICE_ID?: string;
YEARLY_PRICE_ID?: string;
NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL?: string;
NEXT_PUBLIC_TRIAL_PERIOD_DAYS?: string;
- BASE_URL?: string;
// Proxy settings
PROXY?: string;
@@ -80,6 +83,13 @@ declare global {
AUTH0_CLIENT_SECRET?: string;
AUTH0_CLIENT_ID?: string;
+ // Authelia
+ NEXT_PUBLIC_AUTHELIA_ENABLED?: string;
+ AUTHELIA_CUSTOM_NAME?: string;
+ AUTHELIA_CLIENT_ID?: string;
+ AUTHELIA_CLIENT_SECRET?: string;
+ AUTHELIA_WELLKNOWN_URL?: string;
+
// Authentik
NEXT_PUBLIC_AUTHENTIK_ENABLED?: string;
AUTHENTIK_CUSTOM_NAME?: string;
diff --git a/types/global.ts b/types/global.ts
index e23cc98..ea07125 100644
--- a/types/global.ts
+++ b/types/global.ts
@@ -7,10 +7,16 @@ type OptionalExcluding
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Verify your new Linkwarden email address
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Verify Email
+
+
+
+
+