From 326939feee2f608f99e6efa2c6a594e079ca6d5e Mon Sep 17 00:00:00 2001 From: ZL Asica <40444637+ZL-Asica@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:01:49 -0600 Subject: [PATCH 1/7] feat: add config to switch case sensitivities --- components/dashboard/links/Editor.vue | 4 +++- nuxt.config.ts | 1 + schemas/link.ts | 7 ++++++- server/middleware/1.redirect.ts | 8 ++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/components/dashboard/links/Editor.vue b/components/dashboard/links/Editor.vue index 4f7fb3b6..d4eedddb 100644 --- a/components/dashboard/links/Editor.vue +++ b/components/dashboard/links/Editor.vue @@ -102,10 +102,12 @@ onMounted(() => { } }) +const { caseSensitive } = useRuntimeConfig() + async function onSubmit(formData) { const link = { url: formData.url, - slug: formData.slug, + slug: caseSensitive ? formData.slug : formData.slug.toLowerCase(), ...(formData.optional || []), expiration: formData.optional?.expiration ? date2unix(formData.optional?.expiration, 'end') : undefined, } diff --git a/nuxt.config.ts b/nuxt.config.ts index 049414ac..30beb159 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -60,6 +60,7 @@ export default defineNuxtConfig({ dataset: 'sink', aiModel: '@cf/meta/llama-3.1-8b-instruct', aiPrompt: `You are a URL shortening assistant, please shorten the URL provided by the user into a SLUG. The SLUG information must come from the URL itself, do not make any assumptions. A SLUG is human-readable and should not exceed three words and can be validated using regular expressions {slugRegex} . Only the best one is returned, the format must be JSON reference {"slug": "example-slug"}`, + caseSensitive: false, public: { previewMode: '', slugDefaultLength: '6', diff --git a/schemas/link.ts b/schemas/link.ts index fe74492a..763c650c 100644 --- a/schemas/link.ts +++ b/schemas/link.ts @@ -2,10 +2,15 @@ import { z } from 'zod' import { customAlphabet } from 'nanoid' const { slugRegex } = useAppConfig() +const { caseSensitive } = useRuntimeConfig() const slugDefaultLength = +useRuntimeConfig().public.slugDefaultLength -export const nanoid = (length: number = slugDefaultLength) => customAlphabet('23456789abcdefghjkmnpqrstuvwxyz', length) +export function nanoid(length: number = slugDefaultLength) { + return caseSensitive + ? customAlphabet('23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ', length) + : customAlphabet('23456789abcdefghjkmnpqrstuvwxyz', length) +} export const LinkSchema = z.object({ id: z.string().trim().max(26).default(nanoid(10)), diff --git a/server/middleware/1.redirect.ts b/server/middleware/1.redirect.ts index 297098d9..99ea21f6 100644 --- a/server/middleware/1.redirect.ts +++ b/server/middleware/1.redirect.ts @@ -5,7 +5,7 @@ import type { LinkSchema } from '@/schemas/link' export default eventHandler(async (event) => { const { pathname: slug } = parsePath(event.path.replace(/^\/|\/$/g, '')) // remove leading and trailing slashes const { slugRegex, reserveSlug } = useAppConfig(event) - const { homeURL, linkCacheTtl, redirectWithQuery } = useRuntimeConfig(event) + const { homeURL, linkCacheTtl, redirectWithQuery, caseSensitive } = useRuntimeConfig(event) const { cloudflare } = event.context if (event.path === '/' && homeURL) @@ -13,7 +13,11 @@ export default eventHandler(async (event) => { if (slug && !reserveSlug.includes(slug) && slugRegex.test(slug) && cloudflare) { const { KV } = cloudflare.env - const link: z.infer | null = await KV.get(`link:${slug}`, { type: 'json', cacheTtl: linkCacheTtl }) + const link: z.infer | null = await KV.get( + `link:${caseSensitive ? slug : slug.toLowerCase()}`, + { type: 'json', cacheTtl: linkCacheTtl }, + ) + if (link) { event.context.link = link try { From cd7e3022d576eb9834e32e07957d93c780ad7167 Mon Sep 17 00:00:00 2001 From: ZL Asica <40444637+ZL-Asica@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:02:59 -0600 Subject: [PATCH 2/7] chore: fix wrong key in vscode setting For eslint version > 9 no need to add .experimental --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 898ec90f..68d55ed1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { // Enable ESlint flat config support - "eslint.experimental.useFlatConfig": true + "eslint.useFlatConfig": true } From e51e912972e5e6fec82c7f68c32cb37a99f346ef Mon Sep 17 00:00:00 2001 From: ZL Asica <40444637+ZL-Asica@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:22:45 -0600 Subject: [PATCH 3/7] doc: update docs to reflect the new feature. --- README.md | 1 + docs/faqs.md | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/README.md b/README.md index 14424b87..82860a35 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ - **Customizable Slug:** Support for personalized slugs. - **🪄 AI Slug:** Leverage AI to generate slugs. - **Link Expiration:** Set expiration dates for your links. +- **Switch between case sensitivity:** Toggle between case sensitivity for slugs. ## 🪧 Demo diff --git a/docs/faqs.md b/docs/faqs.md index aa6cde5c..159536b8 100644 --- a/docs/faqs.md +++ b/docs/faqs.md @@ -32,3 +32,13 @@ Of course. Please set the environment variable `NUXT_HOME_URL` to your blog or o ## 5. Why can't I see statistics after deploying with NuxtHub? NuxtHub's ANALYTICS points to its dataset, you need to set the `NUXT_DATASET` environment variable to point to the same dataset. + +## 6. Why are links always case-insensitive? + +This is a feature of Sink. By default, we automatically convert all links to lowercase to avoid case-sensitive issues and improve usability. This ensures users don’t encounter errors due to accidental capitalization differences. + +However, you can disable this feature by setting the `NUXT_CASE_SENSITIVE` environment variable to `true`. + +### What happens when `NUXT_CASE_SENSITIVE` is `true`? + +Newly generated links will be case-sensitive, treating `MyLink` and `mylink` as distinct. Randomly generated slugs will include both uppercase and lowercase characters, offering a larger pool of unique combinations (but not user-friendly that why we default to non-case-sensitive). From 902fc1bef5a6dee5f78ec045c496b57dbc752157 Mon Sep 17 00:00:00 2001 From: ZL Asica <40444637+ZL-Asica@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:19:40 -0600 Subject: [PATCH 4/7] fix: forward compability for case sensitive --- server/middleware/1.redirect.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server/middleware/1.redirect.ts b/server/middleware/1.redirect.ts index 99ea21f6..a8de17a1 100644 --- a/server/middleware/1.redirect.ts +++ b/server/middleware/1.redirect.ts @@ -13,10 +13,16 @@ export default eventHandler(async (event) => { if (slug && !reserveSlug.includes(slug) && slugRegex.test(slug) && cloudflare) { const { KV } = cloudflare.env - const link: z.infer | null = await KV.get( - `link:${caseSensitive ? slug : slug.toLowerCase()}`, - { type: 'json', cacheTtl: linkCacheTtl }, - ) + + let link: z.infer | null = null + + const getLink = async (key: string) => + await KV.get(`link:${key}`, { type: 'json', cacheTtl: linkCacheTtl }) + + link = await getLink(slug) + if (!caseSensitive && !link) { + link = await getLink(slug.toLowerCase()) + } if (link) { event.context.link = link From 899323faf6be0696d5dbe03ed9ff65dda14d092d Mon Sep 17 00:00:00 2001 From: ZL Asica <40444637+ZL-Asica@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:11:07 -0600 Subject: [PATCH 5/7] chore: pre-check if slug is same before and after lowercase. --- server/middleware/1.redirect.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/middleware/1.redirect.ts b/server/middleware/1.redirect.ts index a8de17a1..1c3ce805 100644 --- a/server/middleware/1.redirect.ts +++ b/server/middleware/1.redirect.ts @@ -20,8 +20,9 @@ export default eventHandler(async (event) => { await KV.get(`link:${key}`, { type: 'json', cacheTtl: linkCacheTtl }) link = await getLink(slug) - if (!caseSensitive && !link) { - link = await getLink(slug.toLowerCase()) + const lowercaseSlug = slug.toLowerCase() + if (!caseSensitive && !link && lowercaseSlug !== slug) { + link = await getLink(lowercaseSlug) } if (link) { From 7ab62c6a07547026d82796ebe35b64ba53493610 Mon Sep 17 00:00:00 2001 From: ccbikai Date: Sun, 15 Dec 2024 20:38:32 +0800 Subject: [PATCH 6/7] feat: add debug log for case-insensitive slug fallback Adds logging when a case-insensitive URL slug fallback is attempted. This helps track when and how often the fallback mechanism is used. Also improves code readability by renaming variable to match camelCase convention. --- server/middleware/1.redirect.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/middleware/1.redirect.ts b/server/middleware/1.redirect.ts index 1c3ce805..aeebad33 100644 --- a/server/middleware/1.redirect.ts +++ b/server/middleware/1.redirect.ts @@ -20,9 +20,12 @@ export default eventHandler(async (event) => { await KV.get(`link:${key}`, { type: 'json', cacheTtl: linkCacheTtl }) link = await getLink(slug) - const lowercaseSlug = slug.toLowerCase() - if (!caseSensitive && !link && lowercaseSlug !== slug) { - link = await getLink(lowercaseSlug) + + // fallback to lowercase slug if caseSensitive is false and the slug is not found + const lowerCaseSlug = slug.toLowerCase() + if (!caseSensitive && !link && lowerCaseSlug !== slug) { + console.log('lowerCaseSlug fallback:', `slug:${slug} lowerCaseSlug:${lowerCaseSlug}`) + link = await getLink(lowerCaseSlug) } if (link) { From 973d51d4edc3ad11f39b869c9e8c081e368beb28 Mon Sep 17 00:00:00 2001 From: ccbikai Date: Sun, 15 Dec 2024 20:39:34 +0800 Subject: [PATCH 7/7] docs: consolidate slug customization features Merges the case sensitivity feature description into the customizable slug feature section for better organization and clarity. This consolidation simplifies the feature list while maintaining all functionality information --- README.md | 3 +-- components/home/Features.vue | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 82860a35..1a635cdb 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,9 @@ - **URL Shortening:** Compress your URLs to their minimal length. - **Analytics:** Monitor link analytics and gather insightful statistics. - **Serverless:** Deploy without the need for traditional servers. -- **Customizable Slug:** Support for personalized slugs. +- **Customizable Slug:** Support for personalized slugs and case sensitivity. - **🪄 AI Slug:** Leverage AI to generate slugs. - **Link Expiration:** Set expiration dates for your links. -- **Switch between case sensitivity:** Toggle between case sensitivity for slugs. ## 🪧 Demo diff --git a/components/home/Features.vue b/components/home/Features.vue index d61d183f..c983a6db 100644 --- a/components/home/Features.vue +++ b/components/home/Features.vue @@ -23,7 +23,7 @@ const features = ref([ { title: 'Customizable Slug', description: - 'Support for personalized slugs.', + 'Support for personalized slugs and case sensitivity.', icon: Paintbrush, }, {