選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 

93 行
2.9 KiB

  1. import type { Metadata } from "next";
  2. import { GeistSans } from "geist/font/sans";
  3. import { GeistMono } from "geist/font/mono";
  4. import fs from "fs/promises";
  5. import path from "path";
  6. import { getPortals } from "@/lib/db";
  7. import { DEFAULT_FONT } from "@/lib/config";
  8. import "./globals.css";
  9. export const metadata: Metadata = {
  10. title: "Captive Portal",
  11. description: "Welcome",
  12. };
  13. function fontFormat(ext: string): string {
  14. switch (ext.toLowerCase()) {
  15. case ".woff2": return "woff2";
  16. case ".woff": return "woff";
  17. case ".ttf": return "truetype";
  18. case ".otf": return "opentype";
  19. default: return "woff2";
  20. }
  21. }
  22. async function findSibling(regularFilename: string, suffix: string): Promise<string | null> {
  23. const m = regularFilename.match(/^(.*)(\.(?:woff2?|ttf|otf))$/i);
  24. if (!m) return null;
  25. const [, base, ext] = m;
  26. const lower = suffix.toLowerCase();
  27. const candidates = [
  28. `${base}-${suffix}${ext}`, `${base}-${lower}${ext}`,
  29. `${base}_${suffix}${ext}`, `${base}_${lower}${ext}`,
  30. `${base} ${suffix}${ext}`, `${base} ${lower}${ext}`,
  31. `${base}${suffix}${ext}`, `${base}${lower}${ext}`,
  32. ];
  33. try {
  34. const files = await fs.readdir(path.join(process.cwd(), "data", "fonts"));
  35. for (const c of candidates) {
  36. if (files.includes(c)) return c;
  37. }
  38. } catch {}
  39. return null;
  40. }
  41. export default async function RootLayout({
  42. children,
  43. }: Readonly<{ children: React.ReactNode }>) {
  44. // Leggi il portale per scegliere il font
  45. let chosenFont = DEFAULT_FONT;
  46. try {
  47. const portals = await getPortals();
  48. const portal = portals[0];
  49. if (portal && portal.fontFamily !== undefined) chosenFont = portal.fontFamily;
  50. } catch {}
  51. let fontStyleCss = "";
  52. if (chosenFont) {
  53. const fontUrl = (name: string) => `/api/fonts?name=${encodeURIComponent(name)}`;
  54. const faceBlock = (file: string, weight: 400 | 700, style: 'normal' | 'italic') => `
  55. @font-face {
  56. font-family: 'PortalFont';
  57. src: url('${fontUrl(file)}') format('${fontFormat(path.extname(file))}');
  58. font-style: ${style};
  59. font-weight: ${weight};
  60. font-display: swap;
  61. }`;
  62. const italicFile = await findSibling(chosenFont, 'Italic');
  63. const boldFile = await findSibling(chosenFont, 'Bold');
  64. const boldItalicFile = await findSibling(chosenFont, 'BoldItalic');
  65. fontStyleCss = [
  66. faceBlock(chosenFont, 400, 'normal'),
  67. italicFile ? faceBlock(italicFile, 400, 'italic') : '',
  68. boldFile ? faceBlock(boldFile, 700, 'normal') : '',
  69. boldItalicFile ? faceBlock(boldItalicFile, 700, 'italic') : '',
  70. `\n:root { --font-portal: 'PortalFont', Arial, Helvetica, sans-serif; }\n`,
  71. ].join('');
  72. }
  73. return (
  74. <html
  75. lang="it"
  76. className={`${GeistSans.variable} ${GeistMono.variable} h-full antialiased`}
  77. >
  78. <body className="min-h-full flex flex-col">
  79. {fontStyleCss && <style dangerouslySetInnerHTML={{ __html: fontStyleCss }} />}
  80. {children}
  81. </body>
  82. </html>
  83. );
  84. }