Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

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