Initial commit
This commit is contained in:
commit
3fc22f0ed5
|
@ -0,0 +1,14 @@
|
|||
# PAYLOAD CMS
|
||||
PAYLOAD_API_URL=https://payload.domain.com/api
|
||||
PAYLOAD_USER=myemail@domain.com
|
||||
PAYLOAD_PASSWORD=somepassword123
|
||||
|
||||
PORT=8080
|
||||
MEILI_MASTER_KEY=some_api_keyqs23d1qs6d54qs897qs3
|
||||
MEILI_URL=https://meilisearch.domain.com
|
||||
|
||||
WEBHOOK_TOKEN=someApiTokensd54qs6fd5ar7894q3s52x1cq
|
||||
|
||||
## CACHING
|
||||
DATA_CACHING=true
|
||||
DATA_PRECACHING=true
|
|
@ -0,0 +1,6 @@
|
|||
node_modules
|
||||
server/data.ms
|
||||
server/dumps
|
||||
|
||||
.env
|
||||
.cache
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "src/shared"]
|
||||
path = src/shared
|
||||
url = https://github.com/Accords-Library/shared-library.git
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"editor.rulers": [100],
|
||||
"editor.tabSize": 2,
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"package.json": ".git*, package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, .ncurc.*, .nvmrc, *.config.cjs, *.config.js, *.config.ts, *config.json, .*ignore",
|
||||
".env": ".env.*",
|
||||
"README.md": "*.md"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,562 @@
|
|||
{
|
||||
"name": "meili.accords-library.com",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meili.accords-library.com",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"meilisearch": "^0.41.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.14.9",
|
||||
"tsx": "^4.16.2",
|
||||
"typescript": "^5.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
||||
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
||||
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
||||
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
||||
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
||||
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
||||
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
||||
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
||||
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
||||
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
||||
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz",
|
||||
"integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
|
||||
"integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.21.5",
|
||||
"@esbuild/android-arm": "0.21.5",
|
||||
"@esbuild/android-arm64": "0.21.5",
|
||||
"@esbuild/android-x64": "0.21.5",
|
||||
"@esbuild/darwin-arm64": "0.21.5",
|
||||
"@esbuild/darwin-x64": "0.21.5",
|
||||
"@esbuild/freebsd-arm64": "0.21.5",
|
||||
"@esbuild/freebsd-x64": "0.21.5",
|
||||
"@esbuild/linux-arm": "0.21.5",
|
||||
"@esbuild/linux-arm64": "0.21.5",
|
||||
"@esbuild/linux-ia32": "0.21.5",
|
||||
"@esbuild/linux-loong64": "0.21.5",
|
||||
"@esbuild/linux-mips64el": "0.21.5",
|
||||
"@esbuild/linux-ppc64": "0.21.5",
|
||||
"@esbuild/linux-riscv64": "0.21.5",
|
||||
"@esbuild/linux-s390x": "0.21.5",
|
||||
"@esbuild/linux-x64": "0.21.5",
|
||||
"@esbuild/netbsd-x64": "0.21.5",
|
||||
"@esbuild/openbsd-x64": "0.21.5",
|
||||
"@esbuild/sunos-x64": "0.21.5",
|
||||
"@esbuild/win32-arm64": "0.21.5",
|
||||
"@esbuild/win32-ia32": "0.21.5",
|
||||
"@esbuild/win32-x64": "0.21.5"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz",
|
||||
"integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/meilisearch": {
|
||||
"version": "0.41.0",
|
||||
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.41.0.tgz",
|
||||
"integrity": "sha512-5KcGLxEXD7E+uNO7R68rCbGSHgCqeM3Q3RFFLSsN7ZrIgr8HPDXVAIlP4LHggAZfk0FkSzo8VSXifHCwa2k80g==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "^3.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.16.2",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.16.2.tgz",
|
||||
"integrity": "sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "~0.21.5",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
|
||||
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "meili.accords-library.com",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx watch --env-file=.env src/index.ts",
|
||||
"fetch-submodules": "cd src/shared && git pull && cd ../..",
|
||||
"tsc": "tsc --noEmit",
|
||||
"precommit": "npm run fetch-submodules && npm run tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"meilisearch": "^0.41.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.14.9",
|
||||
"tsx": "^4.16.2",
|
||||
"typescript": "^5.5.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
import { getLogger } from "src/utils/logger";
|
||||
import { writeFile, mkdir, readFile } from "fs/promises";
|
||||
import { existsSync } from "fs";
|
||||
import type { PayloadSDK } from "src/shared/payload/sdk";
|
||||
|
||||
const ON_DISK_ROOT = `.cache/dataCache`;
|
||||
const ON_DISK_RESPONSE_CACHE_FILE = `${ON_DISK_ROOT}/responseCache.json`;
|
||||
|
||||
export class DataCache {
|
||||
private readonly logger = getLogger("[DataCache]");
|
||||
private initialized = false;
|
||||
|
||||
private readonly responseCache = new Map<string, any>();
|
||||
private readonly invalidationMap = new Map<string, Set<string>>();
|
||||
|
||||
private scheduleSaveTimeout: NodeJS.Timeout | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly payload: PayloadSDK,
|
||||
private readonly uncachedPayload: PayloadSDK,
|
||||
private readonly onInvalidate: (urls: string[]) => Promise<void>
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
if (this.initialized) return;
|
||||
|
||||
if (process.env.DATA_PRECACHING === "true") {
|
||||
await this.precache();
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
private async precache() {
|
||||
// Get all keys from CMS
|
||||
const allSDKUrls = (await this.uncachedPayload.getAllSdkUrls()).data.urls;
|
||||
|
||||
// Load cache from disk if available
|
||||
if (existsSync(ON_DISK_RESPONSE_CACHE_FILE)) {
|
||||
this.logger.log("Loading cache from disk...");
|
||||
const buffer = await readFile(ON_DISK_RESPONSE_CACHE_FILE);
|
||||
const data = JSON.parse(buffer.toString()) as [string, any][];
|
||||
for (const [key, value] of data) {
|
||||
// Do not include cache where the key is no longer in the CMS
|
||||
if (!allSDKUrls.includes(key)) continue;
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
const cacheSizeBeforePrecaching = this.responseCache.size;
|
||||
|
||||
for (const url of allSDKUrls) {
|
||||
// Do not precache response if already included in the loaded cache from disk
|
||||
if (this.responseCache.has(url)) continue;
|
||||
try {
|
||||
await this.payload.request(url);
|
||||
} catch {
|
||||
this.logger.warn("Precaching failed for url", url);
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheSizeBeforePrecaching !== this.responseCache.size) {
|
||||
this.scheduleSave();
|
||||
}
|
||||
|
||||
this.logger.log("Precaching completed!", this.responseCache.size, "responses cached");
|
||||
}
|
||||
|
||||
get(url: string) {
|
||||
if (process.env.DATA_CACHING !== "true") return;
|
||||
const cachedResponse = this.responseCache.get(url);
|
||||
if (cachedResponse) {
|
||||
this.logger.log("Retrieved cached response for", url);
|
||||
return structuredClone(cachedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
set(url: string, response: any) {
|
||||
if (process.env.DATA_CACHING !== "true") return;
|
||||
const stringData = JSON.stringify(response);
|
||||
const regex = /[a-f0-9]{24}/g;
|
||||
const ids = [...stringData.matchAll(regex)].map((match) => match[0]);
|
||||
const uniqueIds = [...new Set(ids)];
|
||||
|
||||
uniqueIds.forEach((id) => {
|
||||
const current = this.invalidationMap.get(id);
|
||||
if (current) {
|
||||
current.add(url);
|
||||
} else {
|
||||
this.invalidationMap.set(id, new Set([url]));
|
||||
}
|
||||
});
|
||||
|
||||
this.responseCache.set(url, response);
|
||||
this.logger.log("Cached response for", url);
|
||||
if (this.initialized) {
|
||||
this.scheduleSave();
|
||||
}
|
||||
}
|
||||
|
||||
async invalidate(ids: string[], urls: string[]) {
|
||||
if (process.env.DATA_CACHING !== "true") return;
|
||||
const urlsToInvalidate = new Set<string>(urls);
|
||||
|
||||
ids.forEach((id) => {
|
||||
const urlsForThisId = this.invalidationMap.get(id);
|
||||
if (!urlsForThisId) return;
|
||||
this.invalidationMap.delete(id);
|
||||
[...urlsForThisId].forEach((url) => urlsToInvalidate.add(url));
|
||||
});
|
||||
|
||||
for (const url of urlsToInvalidate) {
|
||||
this.responseCache.delete(url);
|
||||
this.logger.log("Invalidated cache for", url);
|
||||
try {
|
||||
await this.payload.request(url);
|
||||
} catch (e) {
|
||||
this.logger.log("Revalidation fails for", url);
|
||||
}
|
||||
}
|
||||
|
||||
this.onInvalidate([...urlsToInvalidate]);
|
||||
this.logger.log("There are currently", this.responseCache.size, "responses in cache.");
|
||||
if (this.initialized) {
|
||||
this.scheduleSave();
|
||||
}
|
||||
}
|
||||
|
||||
private scheduleSave() {
|
||||
if (this.scheduleSaveTimeout) {
|
||||
clearTimeout(this.scheduleSaveTimeout);
|
||||
}
|
||||
this.scheduleSaveTimeout = setTimeout(() => {
|
||||
this.save();
|
||||
}, 10_000);
|
||||
}
|
||||
|
||||
private async save() {
|
||||
if (!existsSync(ON_DISK_ROOT)) {
|
||||
await mkdir(ON_DISK_ROOT, { recursive: true });
|
||||
}
|
||||
|
||||
const serializedResponseCache = JSON.stringify([...this.responseCache]);
|
||||
await writeFile(ON_DISK_RESPONSE_CACHE_FILE, serializedResponseCache, {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
this.logger.log("Saved", ON_DISK_RESPONSE_CACHE_FILE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import type { Meilisearch } from "meilisearch";
|
||||
import { getMeiliDocumentsFromRequest } from "src/convert";
|
||||
import { MeiliIndexes } from "src/shared/meilisearch/constants";
|
||||
import type {
|
||||
MeiliDocument,
|
||||
MeiliDocumentRequest,
|
||||
} from "src/shared/meilisearch/types";
|
||||
import { getLogger } from "src/utils/logger";
|
||||
|
||||
export class DocumentInvalidator {
|
||||
private readonly logger = getLogger("[DocumentInvalidator]");
|
||||
constructor(private readonly meili: Meilisearch) {}
|
||||
|
||||
async invalidate(urls: string[]) {
|
||||
const index = this.meili.index(MeiliIndexes.DOCUMENT);
|
||||
|
||||
const documentRequests: MeiliDocumentRequest[] = [];
|
||||
|
||||
for (const url of urls) {
|
||||
const result = await index.search(undefined, {
|
||||
filter: `endpointCalled = "${url}"`,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
const doc = result.hits[0] as MeiliDocument | undefined;
|
||||
if (!doc) continue;
|
||||
|
||||
await index.deleteDocument(doc.docId);
|
||||
documentRequests.push(doc);
|
||||
}
|
||||
|
||||
const documents: MeiliDocument[] = [];
|
||||
for (const request of documentRequests) {
|
||||
try {
|
||||
documents.push(...(await getMeiliDocumentsFromRequest(request)));
|
||||
} catch (e) {
|
||||
this.logger.log("Failure to revalidate a document");
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
"Adding",
|
||||
documents.length,
|
||||
"documents to Meilisearch"
|
||||
);
|
||||
|
||||
await index.addDocuments(documents);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
export class TokenCache {
|
||||
private token: string | undefined;
|
||||
private expiration: number | undefined;
|
||||
|
||||
get() {
|
||||
if (!this.token) return undefined;
|
||||
if (!this.expiration || this.expiration < Date.now()) {
|
||||
console.log("[PayloadSDK] No token to be retrieved or the token expired");
|
||||
return undefined;
|
||||
}
|
||||
return this.token;
|
||||
}
|
||||
|
||||
set(newToken: string, newExpiration: number) {
|
||||
this.token = newToken;
|
||||
this.expiration = newExpiration * 1000;
|
||||
const diffInMinutes = Math.floor(
|
||||
(this.expiration - Date.now()) / 1000 / 60
|
||||
);
|
||||
console.log(
|
||||
"[PayloadSDK] New token set. TTL is",
|
||||
diffInMinutes,
|
||||
"minutes."
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
import { payload } from "src/services";
|
||||
import type {
|
||||
MeiliDocument,
|
||||
MeiliDocumentRequest,
|
||||
} from "src/shared/meilisearch/types";
|
||||
import { Collections } from "src/shared/payload/constants";
|
||||
import type {
|
||||
EndpointAudio,
|
||||
EndpointChronologyEvent,
|
||||
EndpointCollectible,
|
||||
EndpointFile,
|
||||
EndpointFolder,
|
||||
EndpointImage,
|
||||
EndpointPage,
|
||||
EndpointRecorder,
|
||||
EndpointVideo,
|
||||
} from "src/shared/payload/endpoint-types";
|
||||
import {
|
||||
formatInlineTitle,
|
||||
formatRichTextContentToString,
|
||||
} from "src/shared/payload/format";
|
||||
import type { PayloadSDKResponse } from "src/shared/payload/sdk";
|
||||
|
||||
const convertPageToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointPage>): MeiliDocument[] =>
|
||||
data.translations.map<MeiliDocument>(
|
||||
({ language, pretitle, title, subtitle, content, summary }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: data.translations.map(({ language }) => language),
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
content: `${
|
||||
summary ? `${formatRichTextContentToString(summary)}\n\n\n` : ""
|
||||
}${formatRichTextContentToString(content)}`,
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Pages,
|
||||
slug: data.slug,
|
||||
endpointCalled,
|
||||
data,
|
||||
})
|
||||
);
|
||||
|
||||
const convertCollectibleToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointCollectible>): MeiliDocument[] =>
|
||||
data.translations.map<MeiliDocument>(
|
||||
({ language, pretitle, title, subtitle, description }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: data.translations.map(({ language }) => language), // Add languages from languages field
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
...(description
|
||||
? { description: formatRichTextContentToString(description) }
|
||||
: {}),
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Collectibles,
|
||||
slug: data.slug,
|
||||
endpointCalled,
|
||||
data,
|
||||
})
|
||||
);
|
||||
|
||||
const convertFolderToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointFolder>): MeiliDocument[] =>
|
||||
data.translations.map<MeiliDocument>(({ language, title, description }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: [],
|
||||
title,
|
||||
...(description
|
||||
? { description: formatRichTextContentToString(description) }
|
||||
: {}),
|
||||
type: Collections.Folders,
|
||||
slug: data.slug,
|
||||
endpointCalled,
|
||||
data,
|
||||
}));
|
||||
|
||||
const convertAudioToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointAudio>): MeiliDocument[] =>
|
||||
data.translations.map<MeiliDocument>(({ language, title, description }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: data.translations.map(({ language }) => language),
|
||||
title,
|
||||
...(description
|
||||
? { description: formatRichTextContentToString(description) }
|
||||
: {}),
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Audios,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
}));
|
||||
|
||||
const convertImageToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointImage>): MeiliDocument[] => {
|
||||
if (data.translations.length > 0) {
|
||||
return data.translations.map<MeiliDocument>(
|
||||
({ language, title, description }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: [],
|
||||
title,
|
||||
...(description
|
||||
? { description: formatRichTextContentToString(description) }
|
||||
: {}),
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Images,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
docId: data.id,
|
||||
distinctId: data.id,
|
||||
languages: [],
|
||||
title: data.filename,
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Images,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
const convertVideoToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointVideo>): MeiliDocument[] =>
|
||||
data.translations.map<MeiliDocument>(({ language, title, description }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: data.translations.map(({ language }) => language),
|
||||
title,
|
||||
...(description
|
||||
? { description: formatRichTextContentToString(description) }
|
||||
: {}),
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Videos,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
}));
|
||||
|
||||
const convertRecorderToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointRecorder>): MeiliDocument[] => {
|
||||
if (data.translations.length > 0) {
|
||||
return data.translations.map<MeiliDocument>(({ language, biography }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: [],
|
||||
title: data.username,
|
||||
...(biography
|
||||
? { description: formatRichTextContentToString(biography) }
|
||||
: {}),
|
||||
type: Collections.Recorders,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
}));
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
docId: data.id,
|
||||
distinctId: data.id,
|
||||
languages: [],
|
||||
title: data.username,
|
||||
type: Collections.Recorders,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
const convertFileToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointFile>): MeiliDocument[] => {
|
||||
if (data.translations.length > 0) {
|
||||
return data.translations.map<MeiliDocument>(
|
||||
({ language, title, description }) => ({
|
||||
docId: `${data.id}_${language}`,
|
||||
distinctId: data.id,
|
||||
languages: [],
|
||||
title,
|
||||
...(description
|
||||
? { description: formatRichTextContentToString(description) }
|
||||
: {}),
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Files,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
docId: data.id,
|
||||
distinctId: data.id,
|
||||
languages: [],
|
||||
title: data.filename,
|
||||
updatedAt: Date.parse(data.updatedAt),
|
||||
type: Collections.Files,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data,
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
const convertChronologyEventToDocument = ({
|
||||
data,
|
||||
endpointCalled,
|
||||
}: PayloadSDKResponse<EndpointChronologyEvent>): MeiliDocument[] =>
|
||||
data.events.flatMap((event, index) =>
|
||||
event.translations.map<MeiliDocument>(
|
||||
({ language, description, title, notes }) => ({
|
||||
docId: `${data.id}_${index}_${language}`,
|
||||
distinctId: `${data.id}_${index}`,
|
||||
languages: event.translations.map(({ language }) => language),
|
||||
...(title ? { title } : {}),
|
||||
...(description || notes
|
||||
? {
|
||||
content: `${
|
||||
description ? formatRichTextContentToString(description) : ""
|
||||
}\n\n${notes ? formatRichTextContentToString(notes) : ""}`,
|
||||
}
|
||||
: {}),
|
||||
type: Collections.ChronologyEvents,
|
||||
id: data.id,
|
||||
endpointCalled,
|
||||
data: { date: data.date, event },
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
export const getMeiliDocumentsFromRequest = async (
|
||||
request: MeiliDocumentRequest
|
||||
): Promise<MeiliDocument[]> => {
|
||||
switch (request.type) {
|
||||
case Collections.Audios:
|
||||
return convertAudioToDocument(await payload.getAudioByID(request.id));
|
||||
case Collections.ChronologyEvents:
|
||||
return convertChronologyEventToDocument(
|
||||
await payload.getChronologyEventByID(request.id)
|
||||
);
|
||||
case Collections.Collectibles:
|
||||
return convertCollectibleToDocument(
|
||||
await payload.getCollectible(request.slug)
|
||||
);
|
||||
case Collections.Files:
|
||||
return convertFileToDocument(await payload.getFileByID(request.id));
|
||||
case Collections.Folders:
|
||||
return convertFolderToDocument(await payload.getFolder(request.slug));
|
||||
case Collections.Images:
|
||||
return convertImageToDocument(await payload.getImageByID(request.id));
|
||||
case Collections.Pages:
|
||||
return convertPageToDocument(await payload.getPage(request.slug));
|
||||
case Collections.Recorders:
|
||||
return convertRecorderToDocument(
|
||||
await payload.getRecorderByID(request.id)
|
||||
);
|
||||
case Collections.Videos:
|
||||
return convertVideoToDocument(await payload.getVideoByID(request.id));
|
||||
}
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
import http from "http";
|
||||
import { synchronizeMeiliDocs } from "./synchro";
|
||||
import type { AfterOperationWebHookMessage } from "src/shared/payload/webhooks";
|
||||
import { webhookHandler } from "src/webhook";
|
||||
import { dataCache } from "src/services";
|
||||
|
||||
await dataCache.init();
|
||||
await synchronizeMeiliDocs();
|
||||
|
||||
export const requestListener: http.RequestListener = async (req, res) => {
|
||||
if (req.method !== "POST") {
|
||||
res.writeHead(405).end("Method Not Allowed. Use POST.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.headers.authorization !== `Bearer ${process.env.WEBHOOK_TOKEN}`) {
|
||||
res.writeHead(403).end("Invalid auth token.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve and parse body
|
||||
const buffers: Uint8Array[] = [];
|
||||
for await (const chunk of req) {
|
||||
buffers.push(chunk);
|
||||
}
|
||||
const message = JSON.parse(
|
||||
Buffer.concat(buffers).toString()
|
||||
) as AfterOperationWebHookMessage;
|
||||
|
||||
// Not awaiting on purpose to respond with a 202 and not block the CMS
|
||||
webhookHandler(message);
|
||||
|
||||
res.writeHead(202).end("Accepted");
|
||||
};
|
||||
|
||||
http.createServer(requestListener).listen(process.env.PORT, () => {
|
||||
console.log(`Server started: http://localhost:${process.env.PORT}`);
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
import { MeiliSearch } from "meilisearch";
|
||||
import { DataCache } from "src/cache/dataCache";
|
||||
import { DocumentInvalidator } from "src/cache/documentCache";
|
||||
import { TokenCache } from "src/cache/tokenCache";
|
||||
import { PayloadSDK } from "src/shared/payload/sdk";
|
||||
|
||||
if (!process.env.MEILI_URL) throw new Error("No MEILI_URL!");
|
||||
if (!process.env.MEILI_MASTER_KEY) throw new Error("No MEILI_MASTER_KEY!");
|
||||
if (!process.env.PAYLOAD_API_URL) throw new Error("No PAYLOAD_API_URL!");
|
||||
if (!process.env.PAYLOAD_USER) throw new Error("No PAYLOAD_USER!");
|
||||
if (!process.env.PAYLOAD_PASSWORD) throw new Error("No PAYLOAD_PASSWORD!");
|
||||
|
||||
export const meili = new MeiliSearch({
|
||||
host: process.env.MEILI_URL,
|
||||
apiKey: process.env.MEILI_MASTER_KEY,
|
||||
});
|
||||
|
||||
const tokenCache = new TokenCache();
|
||||
|
||||
export const payload = new PayloadSDK(
|
||||
process.env.PAYLOAD_API_URL,
|
||||
process.env.PAYLOAD_USER,
|
||||
process.env.PAYLOAD_PASSWORD
|
||||
);
|
||||
payload.addTokenCache(tokenCache);
|
||||
|
||||
export const uncachedPayload = new PayloadSDK(
|
||||
process.env.PAYLOAD_API_URL,
|
||||
process.env.PAYLOAD_USER,
|
||||
process.env.PAYLOAD_PASSWORD
|
||||
);
|
||||
uncachedPayload.addTokenCache(tokenCache);
|
||||
|
||||
const documentInvalidator = new DocumentInvalidator(meili);
|
||||
|
||||
export const dataCache = new DataCache(payload, uncachedPayload, (urls) =>
|
||||
documentInvalidator.invalidate(urls)
|
||||
);
|
||||
payload.addDataCache(dataCache);
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 806543a487319e56cb9f678c9c3a35666f90b82a
|
|
@ -0,0 +1,86 @@
|
|||
import { getMeiliDocumentsFromRequest } from "src/convert";
|
||||
import { meili, uncachedPayload } from "src/services";
|
||||
import { MeiliIndexes } from "src/shared/meilisearch/constants";
|
||||
import type {
|
||||
MeiliDocument,
|
||||
MeiliDocumentRequest,
|
||||
} from "src/shared/meilisearch/types";
|
||||
import { Collections } from "src/shared/payload/constants";
|
||||
|
||||
export const synchronizeMeiliDocs = async () => {
|
||||
const version = await meili.getVersion();
|
||||
console.log("Success connecting to Meili!");
|
||||
console.log("Meili version:", version.pkgVersion);
|
||||
|
||||
const indexes = await meili.getIndexes({ limit: 1_000 });
|
||||
|
||||
await Promise.all(
|
||||
indexes.results.map((index) => {
|
||||
console.log("Deleting index", index.uid);
|
||||
return index.delete();
|
||||
})
|
||||
);
|
||||
|
||||
await meili.createIndex(MeiliIndexes.DOCUMENT, { primaryKey: "docId" });
|
||||
const index = meili.index(MeiliIndexes.DOCUMENT);
|
||||
await index.updatePagination({ maxTotalHits: 100_000 });
|
||||
await index.updateFilterableAttributes([
|
||||
"languages",
|
||||
"type",
|
||||
"endpointCalled",
|
||||
]);
|
||||
await index.updateSortableAttributes(["title", "updatedAt"]);
|
||||
await index.updateSearchableAttributes(["title", "content"]);
|
||||
await index.updateDistinctAttribute("distinctId");
|
||||
// await index.updateDisplayedAttributes(["type", "page"]);
|
||||
|
||||
const allIds = (await uncachedPayload.getAllIds()).data;
|
||||
|
||||
const documentRequests: MeiliDocumentRequest[] = [
|
||||
...allIds.pages.slugs.map((slug) => ({
|
||||
type: Collections.Pages as const,
|
||||
slug,
|
||||
})),
|
||||
...allIds.collectibles.slugs.map((slug) => ({
|
||||
type: Collections.Collectibles as const,
|
||||
slug,
|
||||
})),
|
||||
...allIds.folders.slugs.map((slug) => ({
|
||||
type: Collections.Folders as const,
|
||||
slug,
|
||||
})),
|
||||
...allIds.audios.ids.map((id) => ({
|
||||
type: Collections.Audios as const,
|
||||
id,
|
||||
})),
|
||||
...allIds.images.ids.map((id) => ({
|
||||
type: Collections.Images as const,
|
||||
id,
|
||||
})),
|
||||
...allIds.videos.ids.map((id) => ({
|
||||
type: Collections.Videos as const,
|
||||
id,
|
||||
})),
|
||||
...allIds.files.ids.map((id) => ({
|
||||
type: Collections.Files as const,
|
||||
id,
|
||||
})),
|
||||
...allIds.recorders.ids.map((id) => ({
|
||||
type: Collections.Recorders as const,
|
||||
id,
|
||||
})),
|
||||
...allIds.chronologyEvents.ids.map((id) => ({
|
||||
type: Collections.ChronologyEvents as const,
|
||||
id,
|
||||
})),
|
||||
];
|
||||
|
||||
const documents: MeiliDocument[] = [];
|
||||
for (const request of documentRequests) {
|
||||
documents.push(...(await getMeiliDocumentsFromRequest(request)));
|
||||
}
|
||||
|
||||
console.log("Adding", documents.length, "documents to Meilisearch");
|
||||
|
||||
await index.addDocuments(documents);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
export const getLogger = (prefix: string): Pick<Console, "log" | "error" | "warn" | "debug"> => ({
|
||||
debug: (...message) => console.debug(prefix, ...message),
|
||||
log: (...message) => console.log(prefix, ...message),
|
||||
warn: (...message) => console.warn(prefix, ...message),
|
||||
error: (...message) => console.error(prefix, ...message),
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { dataCache } from "src/services";
|
||||
import type { AfterOperationWebHookMessage } from "src/shared/payload/webhooks";
|
||||
|
||||
export const webhookHandler = async ({
|
||||
id,
|
||||
addedDependantIds,
|
||||
urls,
|
||||
}: AfterOperationWebHookMessage) => {
|
||||
await dataCache.invalidate([...(id ? [id] : []), ...addedDependantIds], urls);
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Enable top-level await, and other modern ESM features.
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
// Enable module resolution without file extensions on relative paths, for things like npm package imports.
|
||||
"moduleResolution": "Bundler",
|
||||
// Allow importing TypeScript files using their native extension (.ts(x)).
|
||||
"allowImportingTsExtensions": true,
|
||||
// Enable JSON imports.
|
||||
"resolveJsonModule": true,
|
||||
// Enforce the usage of type-only imports when needed, which helps avoiding bundling issues.
|
||||
"verbatimModuleSyntax": true,
|
||||
// Ensure that each file can be transpiled without relying on other imports.
|
||||
// This is redundant with the previous option, however it ensures that it's on even if someone disable `verbatimModuleSyntax`
|
||||
"isolatedModules": true,
|
||||
// Astro directly run TypeScript code, no transpilation needed.
|
||||
"noEmit": true,
|
||||
// Report an error when importing a file using a casing different from another import of the same file.
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
// Properly support importing CJS modules in ESM
|
||||
"esModuleInterop": true,
|
||||
// Skip typechecking libraries and .d.ts files
|
||||
"skipLibCheck": true,
|
||||
// Allow JavaScript files to be imported
|
||||
"allowJs": true,
|
||||
// Allow JSX files (or files that are internally considered JSX, like Astro files) to be imported inside `.js` and `.ts` files.
|
||||
"jsx": "preserve",
|
||||
|
||||
// Enable strict mode. This enables a few options at a time, see https://www.typescriptlang.org/tsconfig#strict for a list.
|
||||
"strict": true,
|
||||
|
||||
// Report errors for fallthrough cases in switch statements
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
// Force functions designed to override their parent class to be specified as `override`.
|
||||
"noImplicitOverride": true,
|
||||
// Force functions to specify that they can return `undefined` if a possible code path does not return a value.
|
||||
"noImplicitReturns": true,
|
||||
// Report an error when a variable is declared but never used.
|
||||
"noUnusedLocals": true,
|
||||
// Report an error when a parameter is declared but never used.
|
||||
"noUnusedParameters": true,
|
||||
// Force the usage of the indexed syntax to access fields declared using an index signature.
|
||||
"noUncheckedIndexedAccess": true,
|
||||
// Report an error when the value `undefined` is given to an optional property that doesn't specify `undefined` as a valid value.
|
||||
"exactOptionalPropertyTypes": true,
|
||||
// Report an error for unreachable code instead of just a warning.
|
||||
"allowUnreachableCode": false,
|
||||
// Report an error for unused labels instead of just a warning.
|
||||
"allowUnusedLabels": false,
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"pages/*": ["src/pages/*"],
|
||||
"components/*": ["src/components/*"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue