Add Open Graph support with @vercel/og integration and related updates

This commit is contained in:
Marco Pedone 2025-08-08 11:34:33 +02:00
parent 3c63aa0eb8
commit 8d4f51aaf8
9 changed files with 283 additions and 73 deletions

View file

@ -34,6 +34,7 @@
"@trpc/next": "^11.4.3", "@trpc/next": "^11.4.3",
"@trpc/react-query": "^11.1.0", "@trpc/react-query": "^11.1.0",
"@trpc/server": "^11.1.0", "@trpc/server": "^11.1.0",
"@vercel/og": "^0.8.5",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
@ -6863,6 +6864,15 @@
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==", "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@resvg/resvg-wasm": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@resvg/resvg-wasm/-/resvg-wasm-2.4.0.tgz",
"integrity": "sha512-C7c51Nn4yTxXFKvgh2txJFNweaVcfUPQxwEUFw4aWsCmfiBDJsTSwviIF8EcwjQ6k8bPyMWCl1vw4BdxE569Cg==",
"license": "MPL-2.0",
"engines": {
"node": ">= 10"
}
},
"node_modules/@rtsao/scc": { "node_modules/@rtsao/scc": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@ -6890,6 +6900,22 @@
"url": "https://ko-fi.com/killymxi" "url": "https://ko-fi.com/killymxi"
} }
}, },
"node_modules/@shuding/opentype.js": {
"version": "1.4.0-beta.0",
"resolved": "https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz",
"integrity": "sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==",
"license": "MIT",
"dependencies": {
"fflate": "^0.7.3",
"string.prototype.codepointat": "^0.2.1"
},
"bin": {
"ot": "bin/ot"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/@socket.io/component-emitter": { "node_modules/@socket.io/component-emitter": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
@ -8834,6 +8860,19 @@
"win32" "win32"
] ]
}, },
"node_modules/@vercel/og": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@vercel/og/-/og-0.8.5.tgz",
"integrity": "sha512-fHqnxfBYcwkamlEgcIzaZqL8IHT09hR7FZL7UdMTdGJyoaBzM/dY6ulO5Swi4ig30FrBJI9I2C+GLV9sb9vexA==",
"license": "MPL-2.0",
"dependencies": {
"@resvg/resvg-wasm": "2.4.0",
"satori": "0.16.0"
},
"engines": {
"node": ">=16"
}
},
"node_modules/@webassemblyjs/ast": { "node_modules/@webassemblyjs/ast": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@ -9735,6 +9774,15 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/camelize": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001731", "version": "1.0.30001731",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz",
@ -10213,6 +10261,36 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/css-background-parser": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz",
"integrity": "sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==",
"license": "MIT"
},
"node_modules/css-box-shadow": {
"version": "1.0.0-3",
"resolved": "https://registry.npmjs.org/css-box-shadow/-/css-box-shadow-1.0.0-3.tgz",
"integrity": "sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==",
"license": "MIT"
},
"node_modules/css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
"license": "ISC",
"engines": {
"node": ">=4"
}
},
"node_modules/css-gradient-parser": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/css-gradient-parser/-/css-gradient-parser-0.0.16.tgz",
"integrity": "sha512-3O5QdqgFRUbXvK1x5INf1YkBz1UKSWqrd63vWsum8MNHDBYD5urm3QtxZbKU259OrEXNM26lP/MPY3d1IGkBgA==",
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/css-in-js-utils": { "node_modules/css-in-js-utils": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
@ -10239,6 +10317,17 @@
"url": "https://github.com/sponsors/fb55" "url": "https://github.com/sponsors/fb55"
} }
}, },
"node_modules/css-to-react-native": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
"integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
"license": "MIT",
"dependencies": {
"camelize": "^1.0.0",
"css-color-keywords": "^1.0.0",
"postcss-value-parser": "^4.0.2"
}
},
"node_modules/css-tree": { "node_modules/css-tree": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
@ -10820,6 +10909,15 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/emoji-regex-xs": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-2.0.1.tgz",
"integrity": "sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/engine.io": { "node_modules/engine.io": {
"version": "6.6.4", "version": "6.6.4",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
@ -11180,6 +11278,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/escape-string-regexp": { "node_modules/escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -11919,6 +12023,12 @@
} }
} }
}, },
"node_modules/fflate": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
"integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==",
"license": "MIT"
},
"node_modules/file-entry-cache": { "node_modules/file-entry-cache": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@ -12582,6 +12692,18 @@
"he": "bin/he" "he": "bin/he"
} }
}, },
"node_modules/hex-rgb": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz",
"integrity": "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==",
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/hoist-non-react-statics": { "node_modules/hoist-non-react-statics": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -14127,6 +14249,25 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/linebreak": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
"license": "MIT",
"dependencies": {
"base64-js": "0.0.8",
"unicode-trie": "^2.0.0"
}
},
"node_modules/linebreak/node_modules/base64-js": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/lines-and-columns": { "node_modules/lines-and-columns": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@ -16151,6 +16292,12 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"license": "BlueOak-1.0.0" "license": "BlueOak-1.0.0"
}, },
"node_modules/pako": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
"license": "MIT"
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -16163,6 +16310,16 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/parse-css-color": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz",
"integrity": "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==",
"license": "MIT",
"dependencies": {
"color-name": "^1.1.4",
"hex-rgb": "^4.1.0"
}
},
"node_modules/parse-entities": { "node_modules/parse-entities": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
@ -16618,7 +16775,6 @@
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/postgres-array": { "node_modules/postgres-array": {
@ -18544,6 +18700,28 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/satori": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/satori/-/satori-0.16.0.tgz",
"integrity": "sha512-ZvHN3ygzZ8FuxjSNB+mKBiF/NIoqHzlBGbD0MJiT+MvSsFOvotnWOhdTjxKzhHRT2wPC1QbhLzx2q/Y83VhfYQ==",
"license": "MPL-2.0",
"dependencies": {
"@shuding/opentype.js": "1.4.0-beta.0",
"css-background-parser": "^0.1.0",
"css-box-shadow": "1.0.0-3",
"css-gradient-parser": "^0.0.16",
"css-to-react-native": "^3.0.0",
"emoji-regex-xs": "^2.0.1",
"escape-html": "^1.0.3",
"linebreak": "^1.1.0",
"parse-css-color": "^0.2.1",
"postcss-value-parser": "^4.2.0",
"yoga-layout": "^3.2.1"
},
"engines": {
"node": ">=16"
}
},
"node_modules/scheduler": { "node_modules/scheduler": {
"version": "0.26.0", "version": "0.26.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
@ -19338,6 +19516,12 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/string.prototype.codepointat": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
"integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==",
"license": "MIT"
},
"node_modules/string.prototype.includes": { "node_modules/string.prototype.includes": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@ -19974,6 +20158,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/tiny-inflate": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
"license": "MIT"
},
"node_modules/tiny-invariant": { "node_modules/tiny-invariant": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@ -20421,6 +20611,16 @@
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/unicode-trie": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
"license": "MIT",
"dependencies": {
"pako": "^0.2.5",
"tiny-inflate": "^1.0.0"
}
},
"node_modules/unified": { "node_modules/unified": {
"version": "11.0.5", "version": "11.0.5",
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
@ -21240,6 +21440,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/yoga-layout": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
"integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
"license": "MIT"
},
"node_modules/zod": { "node_modules/zod": {
"version": "4.0.15", "version": "4.0.15",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.0.15.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.15.tgz",

View file

@ -45,6 +45,7 @@
"@trpc/next": "^11.4.3", "@trpc/next": "^11.4.3",
"@trpc/react-query": "^11.1.0", "@trpc/react-query": "^11.1.0",
"@trpc/server": "^11.1.0", "@trpc/server": "^11.1.0",
"@vercel/og": "^0.8.5",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",

View file

@ -0,0 +1,4 @@
User-agent: *
Allow: /api/og/*
Disallow:

View file

@ -37,7 +37,6 @@ import { AnnuncioContext } from "~/providers/AnnuncioProvider";
import { MappaDialogFullscreen } from "~/components/MapDialog"; import { MappaDialogFullscreen } from "~/components/MapDialog";
import { handleConsegna } from "~/lib/annuncio_details"; import { handleConsegna } from "~/lib/annuncio_details";
import { PricingComponent } from "~/components/prezzi"; import { PricingComponent } from "~/components/prezzi";
import type { AnnunciWithCoverImage } from "~/server/controllers/annunci.controller";
import { useMediaQuery } from "~/hooks/use-media-query"; import { useMediaQuery } from "~/hooks/use-media-query";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { import {
@ -57,6 +56,7 @@ import InformationBubble from "~/components/InformationBubble";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { env } from "~/env.mjs"; import { env } from "~/env.mjs";
import type { Annunci } from "~/schemas/public/Annunci";
type AnnuncioProps = { type AnnuncioProps = {
cod: string; cod: string;
@ -107,26 +107,24 @@ const AnnuncioDettaglio: NextPage<AnnuncioProps> = ({
<main> <main>
<Head> <Head>
<title>{`${cod} | ${titolo}`}</title> <title>{`${cod} | ${titolo}`}</title>
<meta property="description" content={description} />
<meta property="og:url" content={window.location.href} /> <meta property="og:url" content={window.location.href} />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:title" content={`${cod} | ${titolo}`} />
<meta property="og:description" content={description} />
<meta property="og:image" content={data.og_url || ""} />
{/* <meta
property="og:image"
content={
data.og_url
? `${env.NEXT_PUBLIC_BASE_URL}/api/og?og=${data.og_url}`
: ""
}
/> */}
<meta <meta
property="og:logo" property="og:logo"
content={`${env.NEXT_PUBLIC_BASE_URL}/Infoalloggi.png`} content={`${env.NEXT_PUBLIC_BASE_URL}/Infoalloggi.png`}
/> />
{data.titolo_it && (
<meta
property="og:title"
content={data.titolo_it}
key="title"
/>
)}
{data.coverImage && (
<meta
property="og:image"
content={`${env.NEXT_PUBLIC_BASE_URL}/api/og/${data.coverImage}`}
key="image"
/>
)}
{typeof window !== "undefined" && ( {typeof window !== "undefined" && (
<meta property="og:url" content={url} /> <meta property="og:url" content={url} />
@ -386,7 +384,7 @@ const CaratteristicheAccordion = ({
); );
}; };
const CardInfos = ({ data }: { data: AnnunciWithCoverImage }) => { const CardInfos = ({ data }: { data: Annunci }) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (

View file

@ -0,0 +1,47 @@
import { ImageResponse } from "@vercel/og";
import type { NextRequest } from "next/server";
export const config = {
runtime: "edge",
};
export default async function handler(request: NextRequest) {
const { searchParams } = request.nextUrl;
const og = searchParams.get("og");
if (!og) {
return new Response("Missing 'cod' parameter", { status: 400 });
}
return new ImageResponse(
(
<div
style={{
display: "flex",
fontSize: 60,
color: "black",
background: "#f6f6f6",
width: "100%",
height: "100%",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
{/* eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text */}
<img
src={og}
//src={`${env.NEXT_PUBLIC_BASE_URL}/go-api/images/get/${cod}`}
style={{
height: "100%",
objectFit: "cover",
}}
/>
</div>
),
{
width: 1200,
height: 630,
},
);
}

View file

@ -1,32 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "GET") {
return res.status(405).json({ error: "Method Not Allowed" });
}
const slug = req.query;
console.log("Fetching image:", slug);
const image = Array.isArray(slug.slug) ? slug.slug.join("/") : slug.slug;
if (typeof image !== "string") {
return res.status(400).json({ error: "Invalid image parameter" });
}
const imageUrl = `${process.env.BACKENDSERVER_URL}/images/get/${image}`;
try {
const response = await fetch(imageUrl);
if (!response.ok) {
return res
.status(response.status)
.json({ error: "Failed to fetch image" });
}
const imageBuffer = await response.arrayBuffer();
res.setHeader("Content-Type", "image/png");
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
res.send(Buffer.from(imageBuffer));
} catch (error) {
console.error("Error fetching image:", error);
return res.status(500).json({ error: "Internal Server Error" });
}
}

View file

@ -35,8 +35,7 @@ import { useTranslation } from "~/providers/I18nProvider";
import { CarouselAnnuncio } from "~/components/annuncio_card"; import { CarouselAnnuncio } from "~/components/annuncio_card";
import { zAnnuncioId } from "~/server/utils/zod_types"; import { zAnnuncioId } from "~/server/utils/zod_types";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import type { AnnunciWithCoverImage } from "~/server/controllers/annunci.controller"; import type { Annunci, AnnunciId } from "~/schemas/public/Annunci";
import type { AnnunciId } from "~/schemas/public/Annunci";
import { DualInputLayout } from "~/components/custom_ui/inputLayouts"; import { DualInputLayout } from "~/components/custom_ui/inputLayouts";
import { PhoneInput } from "~/components/phone-input"; import { PhoneInput } from "~/components/phone-input";
@ -66,7 +65,7 @@ const AnnuncioEdit: NextPageWithLayout<AnnuncioEditProps> = ({
); );
}; };
const AnnuncioEditForm = ({ data }: { data: AnnunciWithCoverImage }) => { const AnnuncioEditForm = ({ data }: { data: Annunci }) => {
const EdiAnnuncioSchema = z.object({ const EdiAnnuncioSchema = z.object({
annuncioId: zAnnuncioId, annuncioId: zAnnuncioId,
titolo_it: z.string().nonempty("Inserisci un titolo valido"), titolo_it: z.string().nonempty("Inserisci un titolo valido"),

View file

@ -104,6 +104,8 @@ export default interface AnnunciTable {
modificato_il: ColumnType<Date | null, Date | string | null, Date | string | null>; modificato_il: ColumnType<Date | null, Date | string | null, Date | string | null>;
consegna: ColumnType<number | null, number | null, number | null>; consegna: ColumnType<number | null, number | null, number | null>;
og_url: ColumnType<string | null, string | null, string | null>;
} }
export type Annunci = Selectable<AnnunciTable>; export type Annunci = Selectable<AnnunciTable>;

View file

@ -1,6 +1,6 @@
import type { Annunci, AnnunciId } from "~/schemas/public/Annunci"; import type { Annunci, AnnunciId } from "~/schemas/public/Annunci";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { createSrc, createSrcset } from "~/server/services/imageServer"; import { createSrcset } from "~/server/services/imageServer";
import { AnnuncioObjectWithImages } from "~/server/services/annunci.service"; import { AnnuncioObjectWithImages } from "~/server/services/annunci.service";
import { zAnnuncioId } from "~/server/utils/zod_types"; import { zAnnuncioId } from "~/server/utils/zod_types";
import { z } from "zod/v4"; import { z } from "zod/v4";
@ -61,12 +61,11 @@ export const getCodici_AnnunciHandler = async (): Promise<string[]> => {
} }
}; };
export type AnnunciWithCoverImage = Annunci & { coverImage: string | null };
export const getAnnunciByCod = async ({ export const getAnnunciByCod = async ({
cod, cod,
}: { }: {
cod: string; cod: string;
}): Promise<AnnunciWithCoverImage | null> => { }): Promise<Annunci | null> => {
try { try {
const annuncio = await db const annuncio = await db
.selectFrom("annunci") .selectFrom("annunci")
@ -80,15 +79,8 @@ export const getAnnunciByCod = async ({
message: "Annuncio non trovato", message: "Annuncio non trovato",
}); });
} }
const coverImage = annuncio.url_immagini
? annuncio.url_immagini[0]
? annuncio.url_immagini[0]
: null
: null;
const item = { ...annuncio, coverImage: coverImage }; return AnnuncioObjectWithImages<typeof annuncio>(annuncio);
return AnnuncioObjectWithImages<typeof item>(item);
} catch { } catch {
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
@ -101,7 +93,7 @@ export const getAnnunciById = async ({
id, id,
}: { }: {
id: AnnunciId; id: AnnunciId;
}): Promise<AnnunciWithCoverImage | null> => { }): Promise<Annunci | null> => {
try { try {
const annuncio = await db const annuncio = await db
.selectFrom("annunci") .selectFrom("annunci")
@ -114,15 +106,8 @@ export const getAnnunciById = async ({
message: "Annuncio non trovato", message: "Annuncio non trovato",
}); });
} }
const coverImage = annuncio.url_immagini
? annuncio.url_immagini[0]
? createSrc(annuncio.url_immagini[0])
: null
: null;
const item = { ...annuncio, coverImage: coverImage }; return AnnuncioObjectWithImages<typeof annuncio>(annuncio);
return AnnuncioObjectWithImages<typeof item>(item);
} catch { } catch {
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",