Switch photo ids to nanoids

This commit is contained in:
Sam Becker 2023-09-19 16:26:49 -05:00
parent 7b66647343
commit 6ff2b16d09
10 changed files with 32 additions and 56 deletions

View File

@ -6,6 +6,7 @@
"exif",
"hgetall",
"hset",
"nanoids",
"nextjs",
"qaub",
"skippable",

View File

@ -23,6 +23,7 @@
"eslint": "8.49.0",
"eslint-config-next": "13.4.19",
"framer-motion": "^10.16.4",
"nanoid": "^5.0.1",
"next": "^13.4.19",
"next-auth": "0.0.0-manual.ffd05533",
"next-themes": "^0.2.1",
@ -30,7 +31,6 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.11.0",
"short-uuid": "^4.2.2",
"sonner": "^0.7.4",
"tailwindcss": "3.3.3",
"ts-exif-parser": "^0.2.2",

27
pnpm-lock.yaml generated
View File

@ -17,6 +17,7 @@ specifiers:
eslint: 8.49.0
eslint-config-next: 13.4.19
framer-motion: ^10.16.4
nanoid: ^5.0.1
next: ^13.4.19
next-auth: 0.0.0-manual.ffd05533
next-themes: ^0.2.1
@ -24,7 +25,6 @@ specifiers:
react: 18.2.0
react-dom: 18.2.0
react-icons: ^4.11.0
short-uuid: ^4.2.2
sonner: ^0.7.4
tailwindcss: 3.3.3
ts-exif-parser: ^0.2.2
@ -47,6 +47,7 @@ dependencies:
eslint: 8.49.0
eslint-config-next: 13.4.19_rngtr6f3b25lvetpihwplgecf4
framer-motion: 10.16.4_biqbaboplfbrettd7655fr4n2y
nanoid: 5.0.1
next: 13.4.19_biqbaboplfbrettd7655fr4n2y
next-auth: 0.0.0-manual.ffd05533_next@13.4.19+react@18.2.0
next-themes: 0.2.1_m2l4knchnqa6bzxbbtfxlzoxdy
@ -54,7 +55,6 @@ dependencies:
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-icons: 4.11.0_react@18.2.0
short-uuid: 4.2.2
sonner: 0.7.4_biqbaboplfbrettd7655fr4n2y
tailwindcss: 3.3.3
ts-exif-parser: 0.2.2
@ -761,10 +761,6 @@ packages:
engines: {node: '>=10'}
dev: false
/any-base/1.1.0:
resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==}
dev: false
/any-promise/1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
dev: false
@ -2524,6 +2520,12 @@ packages:
hasBin: true
dev: false
/nanoid/5.0.1:
resolution: {integrity: sha512-vWeVtV5Cw68aML/QaZvqN/3QQXc6fBfIieAlu05m7FZW2Dgb+3f0xc0TTxuJW+7u30t7iSDTV/j3kVI0oJqIfQ==}
engines: {node: ^18 || >=20}
hasBin: true
dev: false
/natural-compare/1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: false
@ -3184,14 +3186,6 @@ packages:
engines: {node: '>=8'}
dev: false
/short-uuid/4.2.2:
resolution: {integrity: sha512-IE7hDSGV2U/VZoCsjctKX6l5t5ak2jE0+aeGJi3KtvjIUNuZVmHVYUjNBhmo369FIWGDtaieRaO8A83Lvwfpqw==}
engines: {node: '>=8'}
dependencies:
any-base: 1.1.0
uuid: 8.3.2
dev: false
/side-channel/1.0.4:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
dependencies:
@ -3596,11 +3590,6 @@ packages:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: false
/uuid/8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
dev: false
/w3c-xmlserializer/4.0.0:
resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==}
engines: {node: '>=14'}

View File

@ -79,7 +79,7 @@ export default function PhotoOGTile({
</div>}
{(loadingState === 'loading' || loadingState === 'loaded') &&
<img
alt={`OG Image: ${photo.idShort}`}
alt={titleForPhoto(photo)}
className={cc(
'absolute top-0 left-0 right-0 bottom-0 z-0',
'w-full',

View File

@ -7,6 +7,7 @@ import {
import { getOffsetFromExif } from '@/utility/exif';
import { toFixedNumber } from '@/utility/number';
import { convertStringToArray } from '@/utility/string';
import { generateNanoid } from '@/utility/nanoid';
export type PhotoFormData = Record<keyof PhotoDbInsert, string>;
@ -19,21 +20,10 @@ type FormMeta = {
hideTemporarily?: boolean
};
const FORM_METADATA: Record<
// Display short ids as a convenience
// even though they're not part of form data
keyof PhotoFormData & { idShort?: string },
FormMeta
> = {
const FORM_METADATA: Record<keyof PhotoFormData, FormMeta> = {
title: { label: 'title' },
tags: { label: 'tags', note: 'comma-separated values' },
id: { label: 'id', readOnly: true, hideIfEmpty: true },
idShort: {
label: 'short id',
readOnly: true,
hideIfEmpty: true,
note: 'Auto-generated by id',
},
url: { label: 'url', readOnly: true },
extension: { label: 'extension', readOnly: true },
aspectRatio: { label: 'aspect ratio', readOnly: true },
@ -122,7 +112,7 @@ export const convertFormDataToPhoto = (
return {
...photoForm,
...(generateId && !photoForm.id) && { id: crypto.randomUUID() },
...(generateId && !photoForm.id) && { id: generateNanoid() },
// convert form strings to arrays
tags: convertStringToArray(photoForm.tags),
// Convert form strings to numbers

View File

@ -12,9 +12,6 @@ import {
} from '@/utility/exif';
import camelcaseKeys from 'camelcase-keys';
import { Metadata } from 'next';
import short from 'short-uuid';
const translator = short();
export const GRID_THUMBNAILS_TO_SHOW_MAX = 12;
@ -58,7 +55,6 @@ export interface PhotoDb extends Omit<PhotoDbInsert, 'takenAt' | 'tags'> {
// Parsed db response
export interface Photo extends PhotoDb {
idShort?: string
focalLengthFormatted?: string
focalLengthIn35MmFormatFormatted?: string
fNumberFormatted?: string
@ -75,8 +71,6 @@ export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => {
) as unknown as PhotoDb;
return {
...photoDb,
idShort:
translator.fromUUID(photoDb.id),
tags: photoDb.tags ?? [],
focalLengthFormatted:
formatFocalLength(photoDb.focalLength),
@ -173,10 +167,8 @@ const PHOTO_ID_FORWARDING_TABLE: Record<string, string> = JSON.parse(
process.env.PHOTO_ID_FORWARDING_TABLE || '{}'
);
export const translatePhotoId = (shortId: string) => {
const id = PHOTO_ID_FORWARDING_TABLE[shortId] || shortId;
return id.length === 22 ? translator.toUUID(id) : id;
};
export const translatePhotoId = (id: string) =>
PHOTO_ID_FORWARDING_TABLE[id] || id;
export const titleForPhoto = (photo: Photo) =>
photo.title || 'Untitled';

View File

@ -6,7 +6,6 @@ import {
parsePhotoFromDb,
Photo,
} from '@/photo';
import { isValidUUID } from '@/utility/string';
const PHOTO_DEFAULT_LIMIT = 100;
@ -17,7 +16,7 @@ export const convertArrayToPostgresString = (array?: string[]) => array
const sqlCreatePhotosTable = () =>
sql`
CREATE TABLE IF NOT EXISTS photos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
id VARCHAR(8) PRIMARY KEY,
url VARCHAR(255) NOT NULL,
extension VARCHAR(255) NOT NULL,
aspect_ratio REAL DEFAULT 1.5,
@ -44,6 +43,7 @@ const sqlCreatePhotosTable = () =>
)
`;
// Must provide id as 8-character nanoid
export const sqlInsertPhotoIntoDb = (photo: PhotoDbInsert) => {
return sql`
INSERT INTO photos (
@ -256,8 +256,6 @@ export const getPhoto = async (id: string): Promise<Photo | undefined> => {
// Check for photo id forwarding
// and convert short ids to uuids
const photoId = translatePhotoId(id);
return isValidUUID(photoId)
? sqlGetPhotoFromDb(photoId)
.then(photos => photos.length > 0 ? photos[0] : undefined)
: Promise.resolve(undefined);
return sqlGetPhotoFromDb(photoId)
.then(photos => photos.length > 0 ? photos[0] : undefined);
};

View File

@ -13,14 +13,14 @@ export const ABSOLUTE_PATH_FOR_HOME_IMAGE = `${BASE_URL}/home-image`;
export const pathForPhoto = (photo: Photo, tag?: string) =>
tag
? `${pathForTag(tag)}/${photo.idShort}`
: `${PREFIX_PHOTO}/${photo.idShort}`;
? `${pathForTag(tag)}/${photo.id}`
: `${PREFIX_PHOTO}/${photo.id}`;
export const pathForPhotoShare = (photo: Photo, tag?: string) =>
`${pathForPhoto(photo, tag)}/share`;
export const pathForPhotoEdit = (photo: Photo) =>
`${PATH_ADMIN_PHOTOS}/${photo.idShort}/edit`;
`${PATH_ADMIN_PHOTOS}/${photo.id}/edit`;
export const pathForTag = (tag: string) => `${PREFIX_TAG}/${tag}`;

9
src/utility/nanoid.ts Normal file
View File

@ -0,0 +1,9 @@
import { customAlphabet } from 'nanoid';
const NANOID_LENGTH = 8;
const NANOID_ALPHABET =
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
export const generateNanoid =
customAlphabet(NANOID_ALPHABET, NANOID_LENGTH);

View File

@ -1,6 +1,3 @@
export const isValidUUID = (id: string): boolean =>
/^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/i.test(id);
export const convertStringToArray = (
string?: string,
parameterize = true,