mirror of
https://github.com/r-freeman/portfolio.git
synced 2024-11-22 07:25:41 +00:00
Added page views functionality
This commit is contained in:
parent
8feb76efc4
commit
7281397fd5
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
prisma/**/*
|
||||
!prisma/schema.prisma
|
||||
.env
|
||||
|
||||
.idea/
|
||||
|
||||
public/rss
|
||||
|
@ -4,8 +4,8 @@ import rehypePrism from '@mapbox/rehype-prism'
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
pageExtensions: ['jsx', 'js', 'tsx', 'mdx'],
|
||||
reactStrictMode: true,
|
||||
pageExtensions: ['jsx', 'js', 'tsx', 'ts', 'mdx'],
|
||||
reactStrictMode: false,
|
||||
swcMinify: true,
|
||||
experimental: {
|
||||
newNextLinkBehavior: true,
|
||||
@ -15,12 +15,16 @@ const nextConfig = {
|
||||
domains: ['i.scdn.co']
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path',
|
||||
destination: 'https://ryanfreeman.dev/:path/',
|
||||
}
|
||||
]
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path',
|
||||
destination: 'https://ryanfreeman.dev/:path/',
|
||||
}
|
||||
]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
346
package-lock.json
generated
346
package-lock.json
generated
@ -11,10 +11,10 @@
|
||||
"@headlessui/react": "^1.7.7",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
"@next/mdx": "^13.1.1",
|
||||
"@prisma/client": "^4.8.1",
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/mdx": "^2.0.3",
|
||||
"@types/node": "18.11.18",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@ -34,7 +34,12 @@
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.31.3",
|
||||
"swr": "^2.0.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"prisma": "^4.8.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "4.9.4"
|
||||
}
|
||||
},
|
||||
@ -49,6 +54,28 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
|
||||
@ -134,7 +161,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@ -161,8 +187,7 @@
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"peer": true
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.17",
|
||||
@ -613,6 +638,38 @@
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.1.tgz",
|
||||
"integrity": "sha512-d4xhZhETmeXK/yZ7K0KcVOzEfI5YKGGEr4F5SBV04/MU4ncN/HcE28sy3e4Yt8UFW0ZuImKFQJE+9rWt9WbGSQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prisma": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prisma": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.8.1.tgz",
|
||||
"integrity": "sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe.tgz",
|
||||
"integrity": "sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw=="
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||
@ -667,6 +724,30 @@
|
||||
"tailwindcss": ">=3.0.0 || insiders"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@types/acorn": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
||||
@ -2035,6 +2116,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -5284,6 +5371,12 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/markdown-extensions": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
|
||||
@ -7469,6 +7562,23 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.8.1.tgz",
|
||||
"integrity": "sha512-ZMLnSjwulIeYfaU1O6/LF6PEJzxN5par5weykxMykS9Z6ara/j76JH3Yo2AH3bgJbPN4Z6NeCK9s5fDkzf33cg==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "4.8.1"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js",
|
||||
"prisma2": "build/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.27.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
|
||||
@ -8740,6 +8850,73 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node/node_modules/acorn-walk": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node/node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/ts-node/node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tsconfig-paths": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
|
||||
@ -9217,6 +9394,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/validate-npm-package-name": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
|
||||
@ -9737,6 +9920,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
@ -9767,6 +9959,27 @@
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
}
|
||||
},
|
||||
"@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
|
||||
@ -9825,8 +10038,7 @@
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"peer": true
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
@ -9847,8 +10059,7 @@
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"peer": true
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.17",
|
||||
@ -10140,6 +10351,25 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.1.tgz",
|
||||
"integrity": "sha512-d4xhZhETmeXK/yZ7K0KcVOzEfI5YKGGEr4F5SBV04/MU4ncN/HcE28sy3e4Yt8UFW0ZuImKFQJE+9rWt9WbGSQ==",
|
||||
"requires": {
|
||||
"@prisma/engines-version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.8.1.tgz",
|
||||
"integrity": "sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe.tgz",
|
||||
"integrity": "sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw=="
|
||||
},
|
||||
"@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||
@ -10183,6 +10413,30 @@
|
||||
"postcss-selector-parser": "6.0.10"
|
||||
}
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@types/acorn": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
|
||||
@ -11200,6 +11454,12 @@
|
||||
"capture-stack-trace": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -13548,6 +13808,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"devOptional": true
|
||||
},
|
||||
"markdown-extensions": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
|
||||
@ -14992,6 +15258,15 @@
|
||||
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
|
||||
"integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="
|
||||
},
|
||||
"prisma": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.8.1.tgz",
|
||||
"integrity": "sha512-ZMLnSjwulIeYfaU1O6/LF6PEJzxN5par5weykxMykS9Z6ara/j76JH3Yo2AH3bgJbPN4Z6NeCK9s5fDkzf33cg==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "4.8.1"
|
||||
}
|
||||
},
|
||||
"prismjs": {
|
||||
"version": "1.27.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
|
||||
@ -15874,6 +16149,47 @@
|
||||
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",
|
||||
"integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g=="
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn-walk": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"devOptional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tsconfig-paths": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
|
||||
@ -16217,6 +16533,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"devOptional": true
|
||||
},
|
||||
"validate-npm-package-name": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
|
||||
@ -16617,6 +16939,12 @@
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"devOptional": true
|
||||
},
|
||||
"yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
13
package.json
13
package.json
@ -13,10 +13,10 @@
|
||||
"@headlessui/react": "^1.7.7",
|
||||
"@mapbox/rehype-prism": "^0.8.0",
|
||||
"@next/mdx": "^13.1.1",
|
||||
"@prisma/client": "^4.8.1",
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/mdx": "^2.0.3",
|
||||
"@types/node": "18.11.18",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"autoprefixer": "^10.4.13",
|
||||
@ -35,8 +35,13 @@
|
||||
"react-dom": "18.2.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.31.3",
|
||||
"swr": "^2.0.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"typescript": "4.9.4"
|
||||
"swr": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"prisma": "^4.8.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "4.9.4",
|
||||
"tailwindcss": "^3.2.4"
|
||||
}
|
||||
}
|
||||
|
17
prisma/schema.prisma
Normal file
17
prisma/schema.prisma
Normal file
@ -0,0 +1,17 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
|
||||
}
|
||||
|
||||
model views {
|
||||
slug String @id
|
||||
count BigInt @default(1)
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
import {ReactNode} from 'react'
|
||||
import Head from 'next/head'
|
||||
import {usePathname} from 'next/navigation'
|
||||
import {Container} from './Container'
|
||||
import {formatDate} from '@/lib/formatDate'
|
||||
import {Prose} from './Prose'
|
||||
import {ReactNode} from 'react'
|
||||
import {Views} from '@/components/Views'
|
||||
|
||||
type ArticleLayout = {
|
||||
children?: ReactNode
|
||||
@ -12,6 +13,7 @@ type ArticleLayout = {
|
||||
description: string
|
||||
ogImage: string
|
||||
date: string
|
||||
slug: string
|
||||
}
|
||||
|
||||
export function ArticleLayout({
|
||||
@ -20,7 +22,8 @@ export function ArticleLayout({
|
||||
title,
|
||||
description,
|
||||
ogImage,
|
||||
date
|
||||
date,
|
||||
slug
|
||||
}: ArticleLayout) {
|
||||
const pathname = usePathname()
|
||||
|
||||
@ -90,12 +93,12 @@ export function ArticleLayout({
|
||||
<h1 className="mt-6 text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
{title}
|
||||
</h1>
|
||||
<time
|
||||
dateTime={date}
|
||||
className="order-first flex items-center text-base text-zinc-500 dark:text-zinc-400"
|
||||
>
|
||||
<span>{formatDate(date)}</span>
|
||||
</time>
|
||||
<p className="order-first text-base text-zinc-500 dark:text-zinc-400">
|
||||
<time dateTime={date}>
|
||||
<span>{formatDate(date)}</span>
|
||||
</time>
|
||||
{' '}·{' '}<Views slug={slug}/>
|
||||
</p>
|
||||
</header>
|
||||
<Prose className="mt-8">{children}</Prose>
|
||||
</article>
|
||||
|
33
src/components/Views.tsx
Normal file
33
src/components/Views.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import {ElementType, useEffect} from 'react'
|
||||
import useSWR from 'swr'
|
||||
import fetcher from '@/lib/fetcher'
|
||||
|
||||
function numberFormat(value: number) {
|
||||
return new Intl.NumberFormat('en', {
|
||||
notation: 'compact'
|
||||
}).format(value)
|
||||
}
|
||||
|
||||
type ViewsType = {
|
||||
views: string
|
||||
}
|
||||
|
||||
export function Views({as: Component = 'span', slug}: { as?: ElementType, slug: string }) {
|
||||
const {data} = useSWR<ViewsType>(`/api/views/${slug}`, fetcher)
|
||||
const views = Number(data?.views)
|
||||
|
||||
useEffect(() => {
|
||||
const registerView = () =>
|
||||
fetch(`/api/views/${slug}`, {
|
||||
method: 'POST'
|
||||
})
|
||||
|
||||
registerView().then(r => r)
|
||||
}, [slug])
|
||||
|
||||
return (
|
||||
<Component>
|
||||
{views > 0 ? `${numberFormat(views)} views` : ''}
|
||||
</Component>
|
||||
)
|
||||
}
|
3
src/lib/createSlug.ts
Normal file
3
src/lib/createSlug.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function createSlug(title: string) {
|
||||
return title.toLowerCase().replace(/\s+/g, '-')
|
||||
}
|
11
src/lib/prisma.ts
Normal file
11
src/lib/prisma.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import {PrismaClient} from '@prisma/client'
|
||||
|
||||
const globalForPrisma = global as unknown as { prisma: PrismaClient }
|
||||
|
||||
export const prisma =
|
||||
globalForPrisma.prisma ||
|
||||
new PrismaClient({
|
||||
log: ['query']
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
47
src/pages/api/views/[slug].ts
Normal file
47
src/pages/api/views/[slug].ts
Normal file
@ -0,0 +1,47 @@
|
||||
import type {NextApiRequest, NextApiResponse} from 'next'
|
||||
import {prisma} from '@/lib/prisma'
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
if (req.query.slug !== undefined) {
|
||||
const slug: string = req.query.slug.toString()
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const newOrUpdatedViews = await prisma.views.upsert({
|
||||
where: {slug},
|
||||
create: {
|
||||
slug
|
||||
},
|
||||
update: {
|
||||
count: {
|
||||
increment: 1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return res.status(200).json({
|
||||
views: newOrUpdatedViews.count.toString()
|
||||
})
|
||||
}
|
||||
|
||||
if (req.method === 'GET') {
|
||||
const views = await prisma.views.findUnique({
|
||||
where: {
|
||||
slug
|
||||
}
|
||||
})
|
||||
|
||||
let count = null
|
||||
if (views !== null) {
|
||||
count = views.count.toString()
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
views: count
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
return res.status(500).json({message: e.message})
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ import {
|
||||
LinkedInIcon,
|
||||
TwitterIcon
|
||||
} from '@/components/SocialIcons'
|
||||
import {InlineLink} from '@/components/InlineLink';
|
||||
import {formatDate} from '@/lib/formatDate'
|
||||
import {generateRssFeed} from '@/lib/generateRssFeed'
|
||||
import {generateSitemap} from '@/lib/generateSitemap'
|
||||
|
@ -1,18 +1,20 @@
|
||||
import {ArticleLayout} from '@/components/ArticleLayout'
|
||||
import {createSlug} from '@/lib/createSlug'
|
||||
|
||||
export const meta = {
|
||||
author: 'Ryan Freeman',
|
||||
date: '2022-12-04',
|
||||
title: 'A personal journey in software engineering',
|
||||
description:
|
||||
'Hello there! If you\'re reading this, you\'ve likely stumbled upon my website — welcome! My name is Ryan Freeman, and I\'m a full-stack developer with a passion for creating intuitive and dynamic web applications.',
|
||||
description: 'Hello there! If you\'re reading this, you\'ve likely stumbled upon my website — welcome! My name is Ryan Freeman, and I\'m a full-stack developer with a passion for creating intuitive and dynamic web applications.',
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout
|
||||
author={meta.author}
|
||||
date={meta.date}
|
||||
title={meta.title}
|
||||
description={meta.description} {...props} />
|
||||
description={meta.description}
|
||||
slug={createSlug(meta.title)}
|
||||
{...props} />
|
||||
|
||||
Hello there!
|
||||
|
||||
|
@ -1,17 +1,20 @@
|
||||
import {ArticleLayout} from '@/components/ArticleLayout'
|
||||
import {createSlug} from '@/lib/createSlug'
|
||||
|
||||
export const meta = {
|
||||
author: 'Ryan Freeman',
|
||||
date: '2023-01-02',
|
||||
title: 'How to add TypeScript to an existing Next.js project',
|
||||
description: 'Next.js includes support for TypeScript by default. To add TypeScript to an existing Next.js project create a tsconfig.json file in the project root with touch tsconfig.json.'
|
||||
description: 'Next.js includes support for TypeScript by default. To add TypeScript to an existing Next.js project create a tsconfig.json file in the project root with touch tsconfig.json.',
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout
|
||||
author={meta.author}
|
||||
date={meta.date}
|
||||
title={meta.title}
|
||||
description={meta.description} {...props} />
|
||||
description={meta.description}
|
||||
slug={createSlug(meta.title)}
|
||||
{...props} />
|
||||
|
||||
# Next.js includes support for TypeScript by default. To add TypeScript to an existing Next.js project create a _tsconfig.json_ file in the project root with `touch tsconfig.json`.
|
||||
Next, run `next dev` and the _tsconfig.json_ file will be populated with the default values, you may customise this configuration to your liking. A file called _next-env.d.ts_ will be created at the project root, this file **should not** be removed or edited.
|
||||
|
Loading…
Reference in New Issue
Block a user