Switch photo ids to nanoids
This commit is contained in:
parent
7b66647343
commit
6ff2b16d09
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -6,6 +6,7 @@
|
||||
"exif",
|
||||
"hgetall",
|
||||
"hset",
|
||||
"nanoids",
|
||||
"nextjs",
|
||||
"qaub",
|
||||
"skippable",
|
||||
|
||||
@ -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
27
pnpm-lock.yaml
generated
@ -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'}
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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
9
src/utility/nanoid.ts
Normal 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);
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user