diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..402dc84b0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,24 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +README.md +config/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..a481ff77a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 diff --git a/.gitignore b/.gitignore index c87c9b392..32e06969e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# homepage +/config diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..926106500 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# Install dependencies only when needed +FROM node:16-alpine AS deps +RUN apk add --no-cache libc6-compat +WORKDIR /app +COPY package.json pnpm-lock.yaml* ./ +RUN yarn global add pnpm +RUN pnpm install + +# Rebuild the source code only when needed +FROM node:16-alpine AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +RUN npm run build + +# Production image, copy all the files and run next +FROM node:16-alpine AS runner +WORKDIR /app +ENV NODE_ENV production +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +COPY --from=builder /app/next.config.js ./ +COPY --from=builder /app/public ./public +COPY --from=builder /app/package.json ./package.json +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +# Since we run in a local environment, we need to accept self signed certificates +ENV NODE_TLS_REJECT_UNAUTHORIZED 0 + +USER nextjs +EXPOSE 3000 +ENV PORT 3000 +CMD ["node", "server.js"] diff --git a/README.md b/README.md index b12f3e33e..f5ffe7347 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,21 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). - ## Getting Started -First, run the development server: +Install NPM packages, this project uses [pnpm](https://pnpm.io/) (and so should you!): ```bash -npm run dev -# or -yarn dev +pnpm install ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. - -[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. - -The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. +Start the development server: -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +```bash +pnpm dev +``` -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! +Open [http://localhost:3000](http://localhost:3000) to start. -## Deploy on Vercel -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +## Configuration -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +Configuration is done in the /config directory using .yaml files. Refer to each config for +the specific configuration options. diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..fd99e2eac --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "./src/", + } +} \ No newline at end of file diff --git a/next.config.js b/next.config.js index ae887958d..9959d2716 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,11 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + output: "standalone", swcMinify: true, -} + images: { + domains: ["cdn.jsdelivr.net"], + }, +}; -module.exports = nextConfig +module.exports = nextConfig; diff --git a/package.json b/package.json index 8717cedf6..c8646bde2 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,25 @@ "lint": "next lint" }, "dependencies": { + "@headlessui/react": "^1.6.6", + "@tailwindcss/forms": "^0.5.2", + "dockerode": "^3.3.3", + "js-yaml": "^4.1.0", + "memory-cache": "^0.2.0", "next": "12.2.5", + "node-os-utils": "^1.3.7", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "react-icons": "^4.4.0", + "sharp": "^0.30.7", + "swr": "^1.3.0" }, "devDependencies": { + "autoprefixer": "^10.4.8", "eslint": "8.22.0", - "eslint-config-next": "12.2.5" + "eslint-config-next": "12.2.5", + "postcss": "^8.4.16", + "tailwindcss": "^3.1.8", + "typescript": "^4.7.4" } } diff --git a/pages/_app.js b/pages/_app.js deleted file mode 100644 index 1e1cec924..000000000 --- a/pages/_app.js +++ /dev/null @@ -1,7 +0,0 @@ -import '../styles/globals.css' - -function MyApp({ Component, pageProps }) { - return -} - -export default MyApp diff --git a/pages/api/hello.js b/pages/api/hello.js deleted file mode 100644 index df63de88f..000000000 --- a/pages/api/hello.js +++ /dev/null @@ -1,5 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction - -export default function handler(req, res) { - res.status(200).json({ name: 'John Doe' }) -} diff --git a/pages/index.js b/pages/index.js deleted file mode 100644 index dc4b64035..000000000 --- a/pages/index.js +++ /dev/null @@ -1,69 +0,0 @@ -import Head from 'next/head' -import Image from 'next/image' -import styles from '../styles/Home.module.css' - -export default function Home() { - return ( -
- - Create Next App - - - - -
-

- Welcome to Next.js! -

- -

- Get started by editing{' '} - pages/index.js -

- -
- -

Documentation →

-

Find in-depth information about Next.js features and API.

-
- - -

Learn →

-

Learn about Next.js in an interactive course with quizzes!

-
- - -

Examples →

-

Discover and deploy boilerplate example Next.js projects.

-
- - -

Deploy →

-

- Instantly deploy your Next.js site to a public URL with Vercel. -

-
-
-
- - -
- ) -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f778d5213..a026931aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,20 +1,46 @@ lockfileVersion: 5.4 specifiers: + '@headlessui/react': ^1.6.6 + '@tailwindcss/forms': ^0.5.2 + autoprefixer: ^10.4.8 + dockerode: ^3.3.3 eslint: 8.22.0 eslint-config-next: 12.2.5 + js-yaml: ^4.1.0 + memory-cache: ^0.2.0 next: 12.2.5 + node-os-utils: ^1.3.7 + postcss: ^8.4.16 react: 18.2.0 react-dom: 18.2.0 + react-icons: ^4.4.0 + sharp: ^0.30.7 + swr: ^1.3.0 + tailwindcss: ^3.1.8 + typescript: ^4.7.4 dependencies: + '@headlessui/react': 1.6.6_biqbaboplfbrettd7655fr4n2y + '@tailwindcss/forms': 0.5.2_tailwindcss@3.1.8 + dockerode: 3.3.3 + js-yaml: 4.1.0 + memory-cache: 0.2.0 next: 12.2.5_biqbaboplfbrettd7655fr4n2y + node-os-utils: 1.3.7 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 + react-icons: 4.4.0_react@18.2.0 + sharp: 0.30.7 + swr: 1.3.0_react@18.2.0 devDependencies: + autoprefixer: 10.4.8_postcss@8.4.16 eslint: 8.22.0 - eslint-config-next: 12.2.5_eslint@8.22.0 + eslint-config-next: 12.2.5_4rv7y5c6xz3vfxwhbrcxxi73bq + postcss: 8.4.16 + tailwindcss: 3.1.8 + typescript: 4.7.4 packages: @@ -50,6 +76,17 @@ packages: - supports-color dev: true + /@headlessui/react/1.6.6_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + dependencies: + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + /@humanwhocodes/config-array/0.10.4: resolution: {integrity: sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==} engines: {node: '>=10.10.0'} @@ -202,12 +239,10 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat/2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk/1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -215,7 +250,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.13.0 - dev: true /@rushstack/eslint-patch/1.1.4: resolution: {integrity: sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==} @@ -227,11 +261,20 @@ packages: tslib: 2.4.0 dev: false + /@tailwindcss/forms/0.5.2_tailwindcss@3.1.8: + resolution: {integrity: sha512-pSrFeJB6Bg1Mrg9CdQW3+hqZXAKsBrSG9MAfFLKy1pVA4Mb4W7C0k7mEhlmS2Dfo/otxrQOET7NJiJ9RrS563w==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: 3.1.8 + dev: false + /@types/json5/0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@typescript-eslint/parser/5.33.0_eslint@8.22.0: + /@typescript-eslint/parser/5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq: resolution: {integrity: sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -243,9 +286,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.33.0 '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0 + '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 debug: 4.3.4 eslint: 8.22.0 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -263,7 +307,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.33.0: + /@typescript-eslint/typescript-estree/5.33.0_typescript@4.7.4: resolution: {integrity: sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -278,7 +322,8 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.7 - tsutils: 3.21.0 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -299,6 +344,22 @@ packages: acorn: 8.8.0 dev: true + /acorn-node/1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + + /acorn-walk/7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + + /acorn/7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + /acorn/8.8.0: resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} engines: {node: '>=0.4.0'} @@ -326,9 +387,18 @@ packages: color-convert: 2.0.1 dev: true + /anymatch/3.1.2: + resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + /arg/5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + /argparse/2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /aria-query/4.2.2: resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} @@ -374,10 +444,32 @@ packages: es-shim-unscopables: 1.0.0 dev: true + /asn1/0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + dependencies: + safer-buffer: 2.1.2 + dev: false + /ast-types-flow/0.0.7: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} dev: true + /autoprefixer/10.4.8_postcss@8.4.16: + resolution: {integrity: sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.3 + caniuse-lite: 1.0.30001375 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.16 + postcss-value-parser: 4.2.0 + dev: true + /axe-core/4.4.3: resolution: {integrity: sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==} engines: {node: '>=4'} @@ -391,6 +483,28 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /base64-js/1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false + + /bcrypt-pbkdf/1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + dependencies: + tweetnacl: 0.14.5 + dev: false + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + + /bl/4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: false + /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -403,8 +517,31 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 + + /browserslist/4.21.3: + resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001375 + electron-to-chromium: 1.4.219 + node-releases: 2.0.6 + update-browserslist-db: 1.0.5_browserslist@4.21.3 dev: true + /buffer/5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + + /buildcheck/0.0.3: + resolution: {integrity: sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==} + engines: {node: '>=10.0.0'} + dev: false + optional: true + /call-bind/1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -417,9 +554,12 @@ packages: engines: {node: '>=6'} dev: true + /camelcase-css/2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + /caniuse-lite/1.0.30001375: resolution: {integrity: sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==} - dev: false /chalk/4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -429,16 +569,47 @@ packages: supports-color: 7.2.0 dev: true + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.2 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + + /chownr/1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: false + /color-convert/2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true + + /color-string/1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color/4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false /concat-map/0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -449,6 +620,16 @@ packages: requiresBuild: true dev: true + /cpu-features/0.0.4: + resolution: {integrity: sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + buildcheck: 0.0.3 + nan: 2.16.0 + dev: false + optional: true + /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -458,6 +639,11 @@ packages: which: 2.0.2 dev: true + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + /damerau-levenshtein/1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true @@ -494,7 +680,18 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true + + /decompress-response/6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + + /deep-extend/0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: false /deep-is/0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -508,6 +705,26 @@ packages: object-keys: 1.1.1 dev: true + /defined/1.0.0: + resolution: {integrity: sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==} + + /detect-libc/2.0.1: + resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + engines: {node: '>=8'} + dev: false + + /detective/5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + dependencies: + acorn-node: 1.8.2 + defined: 1.0.0 + minimist: 1.2.6 + + /didyoumean/1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + /dir-glob/3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -515,6 +732,31 @@ packages: path-type: 4.0.0 dev: true + /dlv/1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + /docker-modem/3.0.5: + resolution: {integrity: sha512-x1E6jxWdtoK3+ifAUWj4w5egPdTDGBpesSCErm+aKET5BnnEOvDtTP6GxcnMB1zZiv2iQ0qJZvJie+1wfIRg6Q==} + engines: {node: '>= 8.0'} + dependencies: + debug: 4.3.4 + readable-stream: 3.6.0 + split-ca: 1.0.1 + ssh2: 1.11.0 + transitivePeerDependencies: + - supports-color + dev: false + + /dockerode/3.3.3: + resolution: {integrity: sha512-lvKV6/NGf2/CYLt5V4c0fd6Fl9XZSCo1Z2HBT9ioKrKLMB2o+gA62Uza8RROpzGvYv57KJx2dKu+ZwSpB//OIA==} + engines: {node: '>= 8.0'} + dependencies: + docker-modem: 3.0.5 + tar-fs: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + /doctrine/2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -529,10 +771,20 @@ packages: esutils: 2.0.3 dev: true + /electron-to-chromium/1.4.219: + resolution: {integrity: sha512-zoQJsXOUw0ZA0YxbjkmzBumAJRtr6je5JySuL/bAoFs0DuLiLJ+5FzRF7/ZayihxR2QcewlRZVm5QZdUhwjOgA==} + dev: true + /emoji-regex/9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true + /end-of-stream/1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + /es-abstract/1.20.1: resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==} engines: {node: '>= 0.4'} @@ -577,12 +829,17 @@ packages: is-symbol: 1.0.4 dev: true + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + /escape-string-regexp/4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: true - /eslint-config-next/12.2.5_eslint@8.22.0: + /eslint-config-next/12.2.5_4rv7y5c6xz3vfxwhbrcxxi73bq: resolution: {integrity: sha512-SOowilkqPzW6DxKp3a3SYlrfPi5Ajs9MIzp9gVfUDxxH9QFM5ElkR1hX5m/iICJuvCbWgQqFBiA3mCMozluniw==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 @@ -593,7 +850,7 @@ packages: dependencies: '@next/eslint-plugin-next': 12.2.5 '@rushstack/eslint-patch': 1.1.4 - '@typescript-eslint/parser': 5.33.0_eslint@8.22.0 + '@typescript-eslint/parser': 5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq eslint: 8.22.0 eslint-import-resolver-node: 0.3.6 eslint-import-resolver-typescript: 2.7.1_2iahngt3u2tkbdlu6s4gkur3pu @@ -601,6 +858,7 @@ packages: eslint-plugin-jsx-a11y: 6.6.1_eslint@8.22.0 eslint-plugin-react: 7.30.1_eslint@8.22.0 eslint-plugin-react-hooks: 4.6.0_eslint@8.22.0 + typescript: 4.7.4 transitivePeerDependencies: - eslint-import-resolver-webpack - supports-color @@ -654,7 +912,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.33.0_eslint@8.22.0 + '@typescript-eslint/parser': 5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq debug: 3.2.7 eslint: 8.22.0 eslint-import-resolver-node: 0.3.6 @@ -673,7 +931,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.33.0_eslint@8.22.0 + '@typescript-eslint/parser': 5.33.0_4rv7y5c6xz3vfxwhbrcxxi73bq array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 @@ -857,6 +1115,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /expand-template/2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + dev: false + /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -870,7 +1133,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -884,7 +1146,6 @@ packages: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} dependencies: reusify: 1.0.4 - dev: true /file-entry-cache/6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} @@ -898,7 +1159,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /find-up/5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} @@ -920,13 +1180,27 @@ packages: resolution: {integrity: sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==} dev: true + /fraction.js/4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: true + + /fs-constants/1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: false + /fs.realpath/1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + optional: true + /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true /function.prototype.name/1.1.5: resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} @@ -962,19 +1236,21 @@ packages: get-intrinsic: 1.1.2 dev: true + /github-from-package/0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + dev: false + /glob-parent/5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent/6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 - dev: true /glob/7.1.7: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} @@ -1053,7 +1329,10 @@ packages: engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 - dev: true + + /ieee754/1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false /ignore/5.2.0: resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} @@ -1082,7 +1361,10 @@ packages: /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true + + /ini/1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: false /internal-slot/1.0.3: resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} @@ -1093,12 +1375,22 @@ packages: side-channel: 1.0.4 dev: true + /is-arrayish/0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + /is-bigint/1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: has-bigints: 1.0.2 dev: true + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + /is-boolean-object/1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} @@ -1116,7 +1408,6 @@ packages: resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} dependencies: has: 1.0.3 - dev: true /is-date-object/1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} @@ -1128,14 +1419,12 @@ packages: /is-extglob/2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-glob/4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-negative-zero/2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} @@ -1152,7 +1441,6 @@ packages: /is-number/7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-regex/1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} @@ -1200,7 +1488,6 @@ packages: hasBin: true dependencies: argparse: 2.0.1 - dev: true /json-schema-traverse/0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1243,6 +1530,10 @@ packages: type-check: 0.4.0 dev: true + /lilconfig/2.0.6: + resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} + engines: {node: '>=10'} + /locate-path/6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1265,12 +1556,14 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true + + /memory-cache/0.2.0: + resolution: {integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==} + dev: false /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /micromatch/4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} @@ -1278,7 +1571,16 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 - dev: true + + /mimic-response/3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + + /mini-svg-data-uri/1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + dev: false /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1288,7 +1590,10 @@ packages: /minimist/1.2.6: resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} - dev: true + + /mkdirp-classic/0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: false /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -1296,16 +1601,23 @@ packages: /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /ms/2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true + /nan/2.16.0: + resolution: {integrity: sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==} + dev: false + optional: true + /nanoid/3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + + /napi-build-utils/1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} dev: false /natural-compare/1.4.0: @@ -1357,11 +1669,43 @@ packages: - babel-plugin-macros dev: false + /node-abi/3.24.0: + resolution: {integrity: sha512-YPG3Co0luSu6GwOBsmIdGW6Wx0NyNDLg/hriIyDllVsNwnI6UeqaWShxC3lbH4LtEQUgoLP3XR1ndXiDAWvmRw==} + engines: {node: '>=10'} + dependencies: + semver: 7.3.7 + dev: false + + /node-addon-api/5.0.0: + resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==} + dev: false + + /node-os-utils/1.3.7: + resolution: {integrity: sha512-fvnX9tZbR7WfCG5BAy3yO/nCLyjVWD6MghEq0z5FDfN+ZXpLWNITBdbifxQkQ25ebr16G0N7eRWJisOcMEHG3Q==} + dev: false + + /node-releases/2.0.6: + resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + /normalize-range/0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} dev: true + /object-hash/3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + /object-inspect/1.12.2: resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} dev: true @@ -1419,7 +1763,6 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: true /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} @@ -1471,7 +1814,6 @@ packages: /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -1480,12 +1822,69 @@ packages: /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: false /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true + + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + /postcss-import/14.1.0_postcss@8.4.16: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.16 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + + /postcss-js/4.0.0_postcss@8.4.16: + resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.3.3 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.16 + + /postcss-load-config/3.1.4_postcss@8.4.16: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.6 + postcss: 8.4.16 + yaml: 1.10.2 + + /postcss-nested/5.0.6_postcss@8.4.16: + resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.16 + postcss-selector-parser: 6.0.10 + + /postcss-selector-parser/6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} /postcss/8.4.14: resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} @@ -1496,6 +1895,33 @@ packages: source-map-js: 1.0.2 dev: false + /postcss/8.4.16: + resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + + /prebuild-install/7.1.1: + resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.1 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.6 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.24.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + /prelude-ls/1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1509,6 +1935,13 @@ packages: react-is: 16.13.1 dev: true + /pump/3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + /punycode/2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} @@ -1516,7 +1949,20 @@ packages: /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + + /quick-lru/5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + /rc/1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.6 + strip-json-comments: 2.0.1 + dev: false /react-dom/18.2.0_react@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} @@ -1528,6 +1974,14 @@ packages: scheduler: 0.23.0 dev: false + /react-icons/4.4.0_react@18.2.0: + resolution: {integrity: sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==} + peerDependencies: + react: '*' + dependencies: + react: 18.2.0 + dev: false + /react-is/16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true @@ -1539,6 +1993,26 @@ packages: loose-envify: 1.4.0 dev: false + /read-cache/1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + + /readable-stream/3.6.0: + resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + /regenerator-runtime/0.13.9: resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} dev: true @@ -1569,7 +2043,6 @@ packages: is-core-module: 2.10.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /resolve/2.0.0-next.4: resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} @@ -1583,7 +2056,6 @@ packages: /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true /rimraf/3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -1596,7 +2068,14 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false /scheduler/0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} @@ -1615,7 +2094,21 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 - dev: true + + /sharp/0.30.7: + resolution: {integrity: sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==} + engines: {node: '>=12.13.0'} + requiresBuild: true + dependencies: + color: 4.2.3 + detect-libc: 2.0.1 + node-addon-api: 5.0.0 + prebuild-install: 7.1.1 + semver: 7.3.7 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false /shebang-command/2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -1637,6 +2130,24 @@ packages: object-inspect: 1.12.2 dev: true + /simple-concat/1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + dev: false + + /simple-get/4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: false + + /simple-swizzle/0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + /slash/3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -1645,6 +2156,21 @@ packages: /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + + /split-ca/1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + dev: false + + /ssh2/1.11.0: + resolution: {integrity: sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==} + engines: {node: '>=10.16.0'} + requiresBuild: true + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.4 + nan: 2.16.0 dev: false /string.prototype.matchall/4.0.7: @@ -1676,6 +2202,12 @@ packages: es-abstract: 1.20.1 dev: true + /string_decoder/1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1688,6 +2220,11 @@ packages: engines: {node: '>=4'} dev: true + /strip-json-comments/2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: false + /strip-json-comments/3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1719,7 +2256,73 @@ packages: /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true + + /swr/1.3.0_react@18.2.0: + resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /tailwindcss/3.1.8: + resolution: {integrity: sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==} + engines: {node: '>=12.13.0'} + hasBin: true + dependencies: + arg: 5.0.2 + chokidar: 3.5.3 + color-name: 1.1.4 + detective: 5.2.1 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.11 + glob-parent: 6.0.2 + is-glob: 4.0.3 + lilconfig: 2.0.6 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.16 + postcss-import: 14.1.0_postcss@8.4.16 + postcss-js: 4.0.0_postcss@8.4.16 + postcss-load-config: 3.1.4_postcss@8.4.16 + postcss-nested: 5.0.6_postcss@8.4.16 + postcss-selector-parser: 6.0.10 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + resolve: 1.22.1 + transitivePeerDependencies: + - ts-node + + /tar-fs/2.0.1: + resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + + /tar-fs/2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + + /tar-stream/2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: false /text-table/0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -1730,7 +2333,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /tsconfig-paths/3.14.1: resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} @@ -1749,15 +2351,26 @@ packages: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} dev: false - /tsutils/3.21.0: + /tsutils/3.21.0_typescript@4.7.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 + typescript: 4.7.4 dev: true + /tunnel-agent/0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /tweetnacl/0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + dev: false + /type-check/0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1770,6 +2383,12 @@ packages: engines: {node: '>=10'} dev: true + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + /unbox-primitive/1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -1779,6 +2398,17 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /update-browserslist-db/1.0.5_browserslist@4.21.3: + resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.3 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -1793,6 +2423,9 @@ packages: react: 18.2.0 dev: false + /util-deprecate/1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /v8-compile-cache/2.3.0: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true @@ -1822,11 +2455,17 @@ packages: /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true + + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true + + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 000000000..33ad091d2 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/components/bookmarks/group.jsx b/src/components/bookmarks/group.jsx new file mode 100644 index 000000000..bf61bcd92 --- /dev/null +++ b/src/components/bookmarks/group.jsx @@ -0,0 +1,15 @@ +import List from "components/bookmarks/list"; + +export default function BookmarksGroup({ group }) { + return ( +
+

+ {group.name} +

+ +
+ ); +} diff --git a/src/components/bookmarks/item.jsx b/src/components/bookmarks/item.jsx new file mode 100644 index 000000000..e55ab145c --- /dev/null +++ b/src/components/bookmarks/item.jsx @@ -0,0 +1,23 @@ +export default function Item({ bookmark }) { + const { hostname } = new URL(bookmark.href); + + return ( +
  • { + window.open(bookmark.href, "_blank").focus(); + }} + key={bookmark.name} + className="mb-3 cursor-pointer flex rounded-md font-medium text-theme-700 hover:text-theme-800 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900 bg-white/50 hover:bg-theme-300/10 dark:bg-white/5 dark:hover:bg-white/10" + > +
    + {bookmark.abbr} +
    +
    +
    {bookmark.name}
    +
    + {hostname} +
    +
    +
  • + ); +} diff --git a/src/components/bookmarks/list.jsx b/src/components/bookmarks/list.jsx new file mode 100644 index 000000000..2c080e02f --- /dev/null +++ b/src/components/bookmarks/list.jsx @@ -0,0 +1,11 @@ +import Item from "components/bookmarks/item"; + +export default function List({ bookmarks }) { + return ( + + ); +} diff --git a/src/components/greeting.jsx b/src/components/greeting.jsx new file mode 100644 index 000000000..5412a0fc2 --- /dev/null +++ b/src/components/greeting.jsx @@ -0,0 +1,20 @@ +export default function Greeting() { + const name = process.env.NEXT_PUBLIC_DISPLAY_NAME; + const hour = new Date().getHours(); + + let day = "day"; + + if (hour < 12) { + day = "morning"; + } else if (hour < 17) { + day = "afternoon"; + } else { + day = "evening"; + } + + return ( +
    + Good {day} +
    + ); +} diff --git a/src/components/modal.jsx b/src/components/modal.jsx new file mode 100644 index 000000000..f683b5e92 --- /dev/null +++ b/src/components/modal.jsx @@ -0,0 +1,68 @@ +import { Fragment, useRef, useState, Children } from "react"; +import { Dialog, Transition } from "@headlessui/react"; + +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +const Modal = ({ Toggle, Content }) => { + const [open, setOpen] = useState(false); + const cancelButtonRef = useRef(null); + + return ( + <> + + + + +
    + + +
    +
    + + + + + +
    +
    +
    +
    + + ); +}; + +const ModalToggle = ({ open, setOpen, children }) => ( +
    setOpen(!open)}>{children}
    +); + +const ModalContent = ({ open, setOpen, children }) => ( +
    {children}
    +); + +Modal.Toggle = ModalToggle; +Modal.Content = ModalContent; + +export default Modal; diff --git a/src/components/services/group.jsx b/src/components/services/group.jsx new file mode 100644 index 000000000..b4f32aa77 --- /dev/null +++ b/src/components/services/group.jsx @@ -0,0 +1,15 @@ +import List from "components/services/list"; + +export default function ServicesGroup({ services }) { + return ( +
    +

    + {services.name} +

    + +
    + ); +} diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx new file mode 100644 index 000000000..797c0c911 --- /dev/null +++ b/src/components/services/item.jsx @@ -0,0 +1,58 @@ +import Image from "next/image"; +import { useState } from "react"; +import { Disclosure, Transition } from "@headlessui/react"; + +import StatsList from "./stats/list"; +import Status from "./status"; +import Widget from "./widget"; + +export default function Item({ service }) { + const [statsOpen, setStatsOpen] = useState(false); + return ( +
  • + +
    +
    +
    { + window.open(service.href, "_blank").focus(); + }} + className="flex-shrink-0 flex items-center justify-center w-12 " + > + logo +
    +
    { + window.open(service.href, "_blank").focus(); + }} + className="flex-1 flex items-center justify-between rounded-r-md " + > +
    + {service.name} +

    {service.description}

    +
    +
    + {service.container && ( + + + + )} +
    + + +
    + +
    +
    + + {service.widget && } +
    +
    +
  • + ); +} diff --git a/src/components/services/list.jsx b/src/components/services/list.jsx new file mode 100644 index 000000000..ee5191373 --- /dev/null +++ b/src/components/services/list.jsx @@ -0,0 +1,11 @@ +import Item from "components/services/item"; + +export default function List({ services }) { + return ( + + ); +} diff --git a/src/components/services/stats/list.jsx b/src/components/services/stats/list.jsx new file mode 100644 index 000000000..cb2cdfaa1 --- /dev/null +++ b/src/components/services/stats/list.jsx @@ -0,0 +1,70 @@ +import useSWR from "swr"; +import { calculateCPUPercent, formatBytes } from "utils/stats-helpers"; +import Stat from "./stat"; + +export default function Stats({ service }) { + // fast + const { data: statusData, error: statusError } = useSWR( + `/api/docker/status/${service.container}/${service.server || ""}`, + { + refreshInterval: 1500, + } + ); + + // takes a full second to collect stats + const { data: statsData, error: statsError } = useSWR( + `/api/docker/stats/${service.container}/${service.server || ""}`, + { + refreshInterval: 1500, + } + ); + + // handle errors first + if (statsError || statusError) { + return ( +
    + +
    + ); + } + + // handle the case where we get a docker error + if (statusData.status !== "running") { + return ( +
    + +
    + ); + } + + // handle the case where the container is offline + if (statusData.status !== "running") { + return ( +
    + +
    + ); + } + + // handle the case where we don't have anything yet + if (!statsData || !statusData) { + return ( +
    + + + + +
    + ); + } + + // we have stats and the container is running + return ( +
    + + + + +
    + ); +} diff --git a/src/components/services/stats/stat.jsx b/src/components/services/stats/stat.jsx new file mode 100644 index 000000000..57674d4c8 --- /dev/null +++ b/src/components/services/stats/stat.jsx @@ -0,0 +1,8 @@ +export default function Stat({ value, label }) { + return ( +
    +
    {value}
    +
    {label}
    +
    + ); +} diff --git a/src/components/services/status.jsx b/src/components/services/status.jsx new file mode 100644 index 000000000..b2c0ea4c0 --- /dev/null +++ b/src/components/services/status.jsx @@ -0,0 +1,29 @@ +import useSWR from "swr"; + +export default function Status({ service }) { + const { data, error } = useSWR( + `/api/docker/status/${service.container}/${service.server || ""}` + ); + + if (error) { + return ( +
    + ); + } + + if (data && data.status === "running") { + return ( +
    + ); + } + + if (data && data.status === "not found") { + return ( + <> +
    + + ); + } + + return
    ; +} diff --git a/src/components/services/widget.jsx b/src/components/services/widget.jsx new file mode 100644 index 000000000..62864cb17 --- /dev/null +++ b/src/components/services/widget.jsx @@ -0,0 +1,27 @@ +import Sonarr from "./widgets/sonarr"; +import Radarr from "./widgets/radarr"; +import Ombi from "./widgets/ombi"; +import Portainer from "./widgets/portainer"; + +const widgetMappings = { + sonarr: Sonarr, + radarr: Radarr, + ombi: Ombi, + portainer: Portainer, +}; + +export default function Widget({ service }) { + const ServiceWidget = widgetMappings[service.widget.type]; + + if (ServiceWidget) { + return ; + } + + return ( +
    +
    + Missing Widget Type: {service.widget.type} +
    +
    + ); +} diff --git a/src/components/services/widgets/ombi.jsx b/src/components/services/widgets/ombi.jsx new file mode 100644 index 000000000..e5527cb2a --- /dev/null +++ b/src/components/services/widgets/ombi.jsx @@ -0,0 +1,71 @@ +import useSWR from "swr"; + +export default function Ombi({ service }) { + const config = service.widget; + + function buildApiUrl(endpoint) { + const { url } = config; + return `${url}/api/v1/${endpoint}`; + } + + const fetcher = (url) => { + return fetch(url, { + method: "GET", + withCredentials: true, + credentials: "include", + headers: { + ApiKey: `${config.key}`, + "Content-Type": "application/json", + }, + }).then((res) => res.json()); + }; + + const { data: statsData, error: statsError } = useSWR( + buildApiUrl(`Request/count`), + fetcher + ); + + if (statsError) { + return ( +
    +
    Ombi API Error
    +
    + ); + } + + if (!statsData) { + return ( +
    +
    +
    -
    +
    COMPLETED
    +
    +
    +
    -
    +
    QUEUED
    +
    +
    +
    -
    +
    TOTAL
    +
    +
    + ); + } + + return ( +
    +
    +
    {statsData.pending}
    +
    PENDING
    +
    +
    +
    {statsData.approved}
    +
    APPROVED
    +
    +
    +
    {statsData.available}
    +
    AVAILABLE
    +
    +
    + ); +} diff --git a/src/components/services/widgets/portainer.jsx b/src/components/services/widgets/portainer.jsx new file mode 100644 index 000000000..7af630dc2 --- /dev/null +++ b/src/components/services/widgets/portainer.jsx @@ -0,0 +1,81 @@ +import useSWR from "swr"; + +export default function Portainer({ service }) { + const config = service.widget; + + function buildApiUrl(endpoint) { + const { url, env } = config; + const reqUrl = new URL(`/api/endpoints/${env}/${endpoint}`, url); + return `/api/proxy?url=${encodeURIComponent(reqUrl)}`; + } + + const fetcher = (url) => { + return fetch(url, { + method: "GET", + withCredentials: true, + credentials: "include", + headers: { + "X-API-Key": `${config.key}`, + "Content-Type": "application/json", + }, + }).then((res) => res.json()); + }; + + const { data: containersData, error: containersError } = useSWR(buildApiUrl(`docker/containers/json`), fetcher); + + if (containersError) { + return ( +
    +
    Portainer API Error
    +
    + ); + } + + if (!containersData) { + return ( +
    +
    +
    -
    +
    RUNNING
    +
    +
    +
    -
    +
    STOPPED
    +
    +
    +
    -
    +
    TOTAL
    +
    +
    + ); + } + + if (containersData.error) { + return ( +
    +
    Portainer API Error
    +
    + ); + } + + const running = containersData.filter((c) => c.State === "running").length; + const stopped = containersData.filter((c) => c.State === "exited").length; + const total = containersData.length; + + return ( +
    +
    +
    {running}
    +
    RUNNING
    +
    +
    +
    {stopped}
    +
    STOPPED
    +
    +
    +
    {total}
    +
    TOTAL
    +
    +
    + ); +} diff --git a/src/components/services/widgets/radarr.jsx b/src/components/services/widgets/radarr.jsx new file mode 100644 index 000000000..562138418 --- /dev/null +++ b/src/components/services/widgets/radarr.jsx @@ -0,0 +1,63 @@ +import useSWR from "swr"; + +export default function Radarr({ service }) { + const config = service.widget; + + function buildApiUrl(endpoint) { + const { url, key } = config; + return `${url}/api/v3/${endpoint}?apikey=${key}`; + } + + const { data: moviesData, error: moviesError } = useSWR(buildApiUrl("movie")); + + const { data: queuedData, error: queuedError } = useSWR( + buildApiUrl("queue/status") + ); + + if (moviesError || queuedError) { + return ( +
    +
    Radarr API Error
    +
    + ); + } + + if (!moviesData || !queuedData) { + return ( +
    +
    +
    -
    +
    WANTED
    +
    +
    +
    -
    +
    QUEUED
    +
    +
    +
    -
    +
    MOVIES
    +
    +
    + ); + } + + const wanted = moviesData.filter((movie) => movie.isAvailable === false); + const have = moviesData.filter((movie) => movie.isAvailable === true); + + return ( +
    +
    +
    {wanted.length}
    +
    WANTED
    +
    +
    +
    {queuedData.totalCount}
    +
    QUEUED
    +
    +
    +
    {moviesData.length}
    +
    MOVIES
    +
    +
    + ); +} diff --git a/src/components/services/widgets/sonarr.jsx b/src/components/services/widgets/sonarr.jsx new file mode 100644 index 000000000..afdda3965 --- /dev/null +++ b/src/components/services/widgets/sonarr.jsx @@ -0,0 +1,53 @@ +import useSWR from "swr"; + +export default function Sonarr({ service }) { + const config = service.widget; + + function buildApiUrl(endpoint) { + const { url, key } = config; + return `${url}/api/v3/${endpoint}?apikey=${key}`; + } + + const { data: wantedData, error: wantedError } = useSWR( + buildApiUrl("wanted/missing") + ); + + const { data: queuedData, error: queuedError } = useSWR(buildApiUrl("queue")); + + const { data: seriesData, error: seriesError } = useSWR( + buildApiUrl("series") + ); + + if (wantedError || queuedError || seriesError) { + return ( +
    +
    Sonarr API Error
    +
    + ); + } + + if (!wantedData || !queuedData || !seriesData) { + return ( +
    +
    Loading
    +
    + ); + } + + return ( +
    +
    +
    {wantedData.totalRecords}
    +
    WANTED
    +
    +
    +
    {queuedData.totalRecords}
    +
    QUEUED
    +
    +
    +
    {seriesData.length}
    +
    SERIES
    +
    +
    + ); +} diff --git a/src/components/theme-toggle.jsx b/src/components/theme-toggle.jsx new file mode 100644 index 000000000..da29d9d2c --- /dev/null +++ b/src/components/theme-toggle.jsx @@ -0,0 +1,35 @@ +import { useContext } from "react"; +import { + MdDarkMode, + MdLightMode, + MdToggleOff, + MdToggleOn, +} from "react-icons/md"; + +import { ThemeContext } from "utils/theme-context"; + +export default function ThemeToggle() { + const { theme, setTheme } = useContext(ThemeContext); + + if (!theme) { + return null; + } + + return ( +
    + + {theme === "dark" ? ( + setTheme(theme === "dark" ? "light" : "dark")} + className="text-theme-800 dark:text-theme-200 w-8 h-8 cursor-pointer" + /> + ) : ( + setTheme(theme === "dark" ? "light" : "dark")} + className="text-theme-800 dark:text-theme-200 w-8 h-8 cursor-pointer" + /> + )} + +
    + ); +} diff --git a/src/components/widget.jsx b/src/components/widget.jsx new file mode 100644 index 000000000..415cc7d40 --- /dev/null +++ b/src/components/widget.jsx @@ -0,0 +1,21 @@ +import Weather from "components/widgets/weather/weather"; +import Resources from "components/widgets/resources/resources"; + +const widgetMappings = { + weather: Weather, + resources: Resources, +}; + +export default function Widget({ widget }) { + const ServiceWidget = widgetMappings[widget.type]; + + if (ServiceWidget) { + return ; + } + + return ( +
    + Missing {widget.type} +
    + ); +} diff --git a/src/components/widgets/resources/resources.jsx b/src/components/widgets/resources/resources.jsx new file mode 100644 index 000000000..d082f5158 --- /dev/null +++ b/src/components/widgets/resources/resources.jsx @@ -0,0 +1,114 @@ +import useSWR from "swr"; +import { FiHardDrive, FiCpu } from "react-icons/fi"; +import { FaMemory } from "react-icons/fa"; + +export default function Resources({ options }) { + const { data, error } = useSWR( + `/api/widgets/resources?disk=${options.disk}`, + { + refreshInterval: 1500, + } + ); + + if (error) { + return
    failed to load
    ; + } + + if (!data) { + return ( + <> + {options.disk && ( +
    + +
    + + - GB free + + + - GB used + +
    +
    + )} + + {options.cpu && ( +
    + +
    + + - Usage + + + - Load + +
    +
    + )} + + {options.memory && ( +
    + +
    + + - GB Used + + + - GB Free + +
    +
    + )} + + ); + } + + if (data.error) { + return
    ; + } + + return ( + <> + {options.disk && ( +
    + +
    + + {Math.round(data.drive.freeGb)} GB free + + + {Math.round(data.drive.totalGb)} GB used + +
    +
    + )} + + {options.cpu && ( +
    + +
    + + {Math.round(data.cpu.usage)}% Usage + + + {Math.round(data.cpu.load * 100) / 100} Load + +
    +
    + )} + + {options.memory && ( +
    + +
    + + {Math.round((data.memory.usedMemMb / 1024) * 100) / 100} GB Used + + + {Math.round((data.memory.freeMemMb / 1024) * 100) / 100} GB Free + +
    +
    + )} + + ); +} diff --git a/src/components/widgets/weather/icon.jsx b/src/components/widgets/weather/icon.jsx new file mode 100644 index 000000000..1a250fbe3 --- /dev/null +++ b/src/components/widgets/weather/icon.jsx @@ -0,0 +1,9 @@ +import mapIcon from "utils/condition-map"; + +export default function Icon({ condition, timeOfDay }) { + const Icon = mapIcon(condition, timeOfDay); + + return ( + + ); +} diff --git a/src/components/widgets/weather/weather.jsx b/src/components/widgets/weather/weather.jsx new file mode 100644 index 000000000..7e42c17d7 --- /dev/null +++ b/src/components/widgets/weather/weather.jsx @@ -0,0 +1,41 @@ +import useSWR from "swr"; +import Icon from "./icon"; + +export default function Weather({ options }) { + const { data, error } = useSWR( + `/api/widgets/weather?lat=${options.latitude}&lon=${options.longitude}&apiKey=${options.apiKey}&duration=${options.cache}`, + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + } + ); + + if (error) { + return
    failed to load
    ; + } + + if (!data) { + return
    ; + } + + if (data.error) { + return
    ; + } + + return ( +
    + +
    + + {Math.round(data.current.temp_f)}° + + + {data.current.condition.text} + +
    +
    + ); +} diff --git a/src/pages/_app.js b/src/pages/_app.js new file mode 100644 index 000000000..2607bc9d3 --- /dev/null +++ b/src/pages/_app.js @@ -0,0 +1,18 @@ +import { SWRConfig } from "swr"; +import "styles/globals.css"; +import "styles/weather-icons.css"; + +function MyApp({ Component, pageProps }) { + return ( + + fetch(resource, init).then((res) => res.json()), + }} + > + + + ); +} + +export default MyApp; diff --git a/src/pages/_document.js b/src/pages/_document.js new file mode 100644 index 000000000..d69d90447 --- /dev/null +++ b/src/pages/_document.js @@ -0,0 +1,24 @@ +import { Html, Head, Main, NextScript } from "next/document"; + +export default function Document() { + return ( + + + + + + + +
    + + + + ); +} diff --git a/src/pages/api/bookmarks.js b/src/pages/api/bookmarks.js new file mode 100644 index 000000000..ee77065de --- /dev/null +++ b/src/pages/api/bookmarks.js @@ -0,0 +1,27 @@ +import { promises as fs } from "fs"; +import path from "path"; +import yaml from "js-yaml"; +import checkAndCopyConfig from "utils/config"; + +export default async function handler(req, res) { + checkAndCopyConfig("bookmarks.yaml"); + + const bookmarksYaml = path.join(process.cwd(), "config", "bookmarks.yaml"); + const fileContents = await fs.readFile(bookmarksYaml, "utf8"); + const bookmarks = yaml.load(fileContents); + + // map easy to write YAML objects into easy to consume JS arrays + const bookmarksArray = bookmarks.map((group) => { + return { + name: Object.keys(group)[0], + bookmarks: group[Object.keys(group)[0]].map((entries) => { + return { + name: Object.keys(entries)[0], + ...entries[Object.keys(entries)[0]][0], + }; + }), + }; + }); + + res.send(bookmarksArray); +} diff --git a/src/pages/api/docker/stats/[...service].js b/src/pages/api/docker/stats/[...service].js new file mode 100644 index 000000000..af6a1a3b8 --- /dev/null +++ b/src/pages/api/docker/stats/[...service].js @@ -0,0 +1,49 @@ +import Docker from "dockerode"; +import getDockerArguments from "utils/docker"; + +export default async function handler(req, res) { + const { service } = req.query; + const [containerName, containerServer] = service; + + if (!containerName && !containerServer) { + res.status(400).send({ + error: "docker query parameters are required", + }); + return; + } + + try { + const docker = new Docker(await getDockerArguments(containerServer)); + const containers = await docker.listContainers(); + + // bad docker connections can result in a object? + // in any case, this ensures the result is the expected array + if (!Array.isArray(containers)) { + return res.status(500).send({ + error: "query failed", + }); + } + + const containerNames = containers.map((container) => { + return container.Names[0].replace(/^\//, ""); + }); + const containerExists = containerNames.includes(containerName); + + if (!containerExists) { + return res.status(404).send({ + error: "not found", + }); + } + + const container = docker.getContainer(containerName); + const stats = await container.stats({ stream: false }); + + return res.status(200).json({ + stats: stats, + }); + } catch { + return res.status(500).send({ + error: "unknown error", + }); + } +} diff --git a/src/pages/api/docker/status/[...service].js b/src/pages/api/docker/status/[...service].js new file mode 100644 index 000000000..a0c8cd5aa --- /dev/null +++ b/src/pages/api/docker/status/[...service].js @@ -0,0 +1,48 @@ +import Docker from "dockerode"; +import getDockerArguments from "utils/docker"; + +export default async function handler(req, res) { + const { service } = req.query; + const [containerName, containerServer] = service; + + if (!containerName && !containerServer) { + return res.status(400).send({ + error: "docker query parameters are required", + }); + } + + try { + const docker = new Docker(await getDockerArguments(containerServer)); + const containers = await docker.listContainers(); + + // bad docker connections can result in a object? + // in any case, this ensures the result is the expected array + if (!Array.isArray(containers)) { + return res.status(500).send({ + error: "query failed", + }); + } + + const containerNames = containers.map((container) => { + return container.Names[0].replace(/^\//, ""); + }); + const containerExists = containerNames.includes(containerName); + + if (!containerExists) { + return res.status(404).send({ + error: "not found", + }); + } + + const container = docker.getContainer(containerName); + const info = await container.inspect(); + + return res.status(200).json({ + status: info.State.Status, + }); + } catch { + return res.status(500).send({ + error: "unknown error", + }); + } +} diff --git a/src/pages/api/proxy.js b/src/pages/api/proxy.js new file mode 100644 index 000000000..26495a4ed --- /dev/null +++ b/src/pages/api/proxy.js @@ -0,0 +1,29 @@ +function pick(object, keys) { + return; +} + +export default async function handler(req, res) { + const headers = ["X-API-Key", "Content-Type", "Authorization"].reduce((obj, key) => { + if (req.headers && req.headers.hasOwnProperty(key.toLowerCase())) { + obj[key] = req.headers[key.toLowerCase()]; + } + return obj; + }, {}); + + try { + const result = await fetch(req.query.url, { + strictSSL: false, + rejectUnhauthorized: false, + method: req.method, + headers: headers, + body: req.method == "GET" || req.method == "HEAD" ? null : req.body, + }).then((res) => res); + + const forward = await result.text(); + return res.status(result.status).send(forward); + } catch { + return res.status(500).send({ + error: "query failed", + }); + } +} diff --git a/src/pages/api/services/index.js b/src/pages/api/services/index.js new file mode 100644 index 000000000..53d508692 --- /dev/null +++ b/src/pages/api/services/index.js @@ -0,0 +1,27 @@ +import { promises as fs } from "fs"; +import path from "path"; +import yaml from "js-yaml"; +import checkAndCopyConfig from "utils/config"; + +export default async function handler(req, res) { + checkAndCopyConfig("services.yaml"); + + const servicesYaml = path.join(process.cwd(), "config", "services.yaml"); + const fileContents = await fs.readFile(servicesYaml, "utf8"); + const services = yaml.load(fileContents); + + // map easy to write YAML objects into easy to consume JS arrays + const servicesArray = services.map((group) => { + return { + name: Object.keys(group)[0], + services: group[Object.keys(group)[0]].map((entries) => { + return { + name: Object.keys(entries)[0], + ...entries[Object.keys(entries)[0]], + }; + }), + }; + }); + + res.send(servicesArray); +} diff --git a/src/pages/api/widgets/index.js b/src/pages/api/widgets/index.js new file mode 100644 index 000000000..98496ad65 --- /dev/null +++ b/src/pages/api/widgets/index.js @@ -0,0 +1,22 @@ +import { promises as fs } from "fs"; +import path from "path"; +import yaml from "js-yaml"; +import checkAndCopyConfig from "utils/config"; + +export default async function handler(req, res) { + checkAndCopyConfig("widgets.yaml"); + + const widgetsYaml = path.join(process.cwd(), "config", "widgets.yaml"); + const fileContents = await fs.readFile(widgetsYaml, "utf8"); + const widgets = yaml.load(fileContents); + + // map easy to write YAML objects into easy to consume JS arrays + const widgetsArray = widgets.map((group) => { + return { + type: Object.keys(group)[0], + options: { ...group[Object.keys(group)[0]] }, + }; + }); + + res.send(widgetsArray); +} diff --git a/src/pages/api/widgets/resources.js b/src/pages/api/widgets/resources.js new file mode 100644 index 000000000..528f50fdb --- /dev/null +++ b/src/pages/api/widgets/resources.js @@ -0,0 +1,14 @@ +import { cpu, drive, mem, netstat } from "node-os-utils"; + +export default async function handler(req, res) { + const { disk } = req.query; + + res.send({ + cpu: { + usage: await cpu.usage(), + load: cpu.loadavgTime(5), + }, + drive: await drive.info(disk || "/"), + memory: await mem.info(), + }); +} diff --git a/src/pages/api/widgets/weather.js b/src/pages/api/widgets/weather.js new file mode 100644 index 000000000..5d1af4b04 --- /dev/null +++ b/src/pages/api/widgets/weather.js @@ -0,0 +1,9 @@ +import cachedFetch from "utils/cached-fetch"; + +export default async function handler(req, res) { + const { lat, lon, apiKey, duration } = req.query; + + const api_url = `http://api.weatherapi.com/v1/current.json?q=${lat},${lon}&key=${apiKey}`; + + res.send(await cachedFetch(api_url, duration)); +} diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 000000000..299585319 --- /dev/null +++ b/src/pages/index.js @@ -0,0 +1,58 @@ +import useSWR from "swr"; +import Head from "next/head"; +import dynamic from "next/dynamic"; + +import { ThemeProvider } from "utils/theme-context"; + +import ServicesGroup from "components/services/group"; +import BookmarksGroup from "components/bookmarks/group"; +import Widget from "components/widget"; + +const ThemeToggle = dynamic(() => import("components/theme-toggle"), { + ssr: false, +}); + +export default function Home() { + const { data: services, error: servicesError } = useSWR("/api/services"); + const { data: bookmarks, error: bookmarksError } = useSWR("/api/bookmarks"); + const { data: widgets, error: widgetsError } = useSWR("/api/widgets"); + + return ( + + + Welcome + +
    +
    + {widgets && ( + <> + {widgets.map((widget) => ( + + ))} + + )} +
    + + {services && ( +
    + {services.map((group) => ( + + ))} +
    + )} + + {bookmarks && ( +
    + {bookmarks.map((group) => ( + + ))} +
    + )} + +
    + +
    +
    +
    + ); +} diff --git a/src/skeleton/bookmarks.yaml b/src/skeleton/bookmarks.yaml new file mode 100644 index 000000000..d553b2e3f --- /dev/null +++ b/src/skeleton/bookmarks.yaml @@ -0,0 +1,34 @@ +- Developer: # Bookmark group title + - Github: # Bookmark title + - abbr: GH # Two letter abbrivation for the bookmark + href: https://github.com/ # URL to link out to + + - StackOverflow: + - abbr: SO + href: https://stackoverflow.com/ + + - DEV: + - abbr: DT + href: https://dev.to/ + +- Social: + - LinkedIn: + - abbr: LI + href: https://linkedin.com/ + + - Twitter: + - abbr: TW + href: https://twitter.com/ + +- Entertainment: + - YouTube: + - abbr: YT + href: https://youtube.com/ + + - Netflix: + - abbr: NF + href: https://netflix.com/ + + - Reddit: + - abbr: RE + href: https://reddit.com/ diff --git a/src/skeleton/docker.yaml b/src/skeleton/docker.yaml new file mode 100644 index 000000000..6e4331939 --- /dev/null +++ b/src/skeleton/docker.yaml @@ -0,0 +1,10 @@ +# Docker host configuration, used for docker integrations +# Can be either a host and port (e.g. 127.0.0.1:2375), +# or a unix socket path (e.g. /var/run/docker.sock) + +my-docker: + host: 127.0.0.1 + port: 2375 + +other-docker: + socket: /var/run/docker.sock diff --git a/src/skeleton/services.yaml b/src/skeleton/services.yaml new file mode 100644 index 000000000..e51e117b9 --- /dev/null +++ b/src/skeleton/services.yaml @@ -0,0 +1,53 @@ +- Media: # Services Group Title + - Emby: # Service Title + icon: emby.png # Icon, see: https://github.com/walkxcode/dashboard-icons or public/icons + href: http://emby.home/ # Link to the service + description: Media server # Description of the service + server: my-docker # Docker server the service is running on + container: emby # Docker container the service is running on + + - Radarr: + icon: radarr.png + href: http://radarr.home/ + description: Movie management + server: my-docker + container: radarr + widget: # Service widget configuration + type: radarr # Widget type, currently supports types radarr, sonarr, ombi or portainer + url: http://radarr.home # Base URL of the service, where the API is located + key: apikeyapikeyapikeyapikey # API key of the service + +- Documents & Files: + - ruTorrent: + icon: rutorrent.png + href: http://rutorrent.home/ + description: Torrent downloader + server: my-docker + container: rutorrent + + - File Browser: + icon: filebrowser.png + href: "#" + description: Media File Management + server: my-docker + container: filebrowser + +- Utilities: + - Media Portainer: + icon: portainer.png + href: http://portainer.home:9443/ + description: Container management + server: my-docker + container: portainer + widget: + type: portainer + url: https://portainer.home:9443 + env: 2 + key: accesskeyaccesskeyaccesskeyaccesskey + + - Traefik: + icon: traefik.png + href: http://traefik.home/ + description: Reverse Proxy + server: my-docker + container: traefik diff --git a/src/skeleton/widgets.yaml b/src/skeleton/widgets.yaml new file mode 100644 index 000000000..e366a7996 --- /dev/null +++ b/src/skeleton/widgets.yaml @@ -0,0 +1,11 @@ +- weather: # type of the widget + latitude: 51.5072 # widget configuration + longitude: 0.1275 + units: metric + apiKey: weather_api_key # get from https://www.weatherapi.com/ + cache: 5 # cache time in minutes + +- resources: + cpu: true + memory: true + disk: / # disk (path) to show usage for diff --git a/src/styles/font/weathericons-regular-webfont.eot b/src/styles/font/weathericons-regular-webfont.eot new file mode 100755 index 000000000..330b7ec7a Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.eot differ diff --git a/src/styles/font/weathericons-regular-webfont.svg b/src/styles/font/weathericons-regular-webfont.svg new file mode 100755 index 000000000..397d7305b --- /dev/null +++ b/src/styles/font/weathericons-regular-webfont.svg @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/styles/font/weathericons-regular-webfont.ttf b/src/styles/font/weathericons-regular-webfont.ttf new file mode 100755 index 000000000..948f0a5d2 Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.ttf differ diff --git a/src/styles/font/weathericons-regular-webfont.woff b/src/styles/font/weathericons-regular-webfont.woff new file mode 100755 index 000000000..e0b2f9483 Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.woff differ diff --git a/src/styles/font/weathericons-regular-webfont.woff2 b/src/styles/font/weathericons-regular-webfont.woff2 new file mode 100755 index 000000000..bb0c19ddf Binary files /dev/null and b/src/styles/font/weathericons-regular-webfont.woff2 differ diff --git a/src/styles/globals.css b/src/styles/globals.css new file mode 100644 index 000000000..e11bf477e --- /dev/null +++ b/src/styles/globals.css @@ -0,0 +1,14 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +#__next { + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} + +body { + font-family: "Poppins", sans-serif; +} diff --git a/src/styles/weather-icons.css b/src/styles/weather-icons.css new file mode 100644 index 000000000..b69f78d1c --- /dev/null +++ b/src/styles/weather-icons.css @@ -0,0 +1,1844 @@ +/*! + * Weather Icons 2.0.10 + * Updated November 1, 2020 + * Weather themed icons for Bootstrap + * Author - Erik Flowers - erik@helloerik.com + * Email: erik@helloerik.com + * Twitter: http://twitter.com/Erik_UX + * ------------------------------------------------------------------------------ + * Maintained at http://erikflowers.github.io/weather-icons + * + * License + * ------------------------------------------------------------------------------ + * - Font licensed under SIL OFL 1.1 - + * http://scripts.sil.org/OFL + * - CSS, SCSS and LESS are licensed under MIT License - + * http://opensource.org/licenses/mit-license.html + * - Documentation licensed under CC BY 3.0 - + * http://creativecommons.org/licenses/by/3.0/ + * - Inspired by and works great as a companion with Font Awesome + * "Font Awesome by Dave Gandy - http://fontawesome.io" + */ +@font-face { + font-family: "weathericons"; + src: url("./font/weathericons-regular-webfont.eot"); + src: url("./font/weathericons-regular-webfont.eot?#iefix") + format("embedded-opentype"), + url("./font/weathericons-regular-webfont.woff2") format("woff2"), + url("./font/weathericons-regular-webfont.woff") format("woff"), + url("./font/weathericons-regular-webfont.ttf") format("truetype"), + url("./font/weathericons-regular-webfont.svg#weather_iconsregular") + format("svg"); + font-weight: normal; + font-style: normal; +} +.wi { + display: inline-block; + font-family: "weathericons"; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.wi-fw { + text-align: center; + width: 1.4em; +} +.wi-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.wi-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.wi-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.wi-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.wi-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +.wi-day-sunny:before { + content: "\f00d"; +} +.wi-day-cloudy:before { + content: "\f002"; +} +.wi-day-cloudy-gusts:before { + content: "\f000"; +} +.wi-day-cloudy-windy:before { + content: "\f001"; +} +.wi-day-fog:before { + content: "\f003"; +} +.wi-day-hail:before { + content: "\f004"; +} +.wi-day-haze:before { + content: "\f0b6"; +} +.wi-day-lightning:before { + content: "\f005"; +} +.wi-day-rain:before { + content: "\f008"; +} +.wi-day-rain-mix:before { + content: "\f006"; +} +.wi-day-rain-wind:before { + content: "\f007"; +} +.wi-day-showers:before { + content: "\f009"; +} +.wi-day-sleet:before { + content: "\f0b2"; +} +.wi-day-sleet-storm:before { + content: "\f068"; +} +.wi-day-snow:before { + content: "\f00a"; +} +.wi-day-snow-thunderstorm:before { + content: "\f06b"; +} +.wi-day-snow-wind:before { + content: "\f065"; +} +.wi-day-sprinkle:before { + content: "\f00b"; +} +.wi-day-storm-showers:before { + content: "\f00e"; +} +.wi-day-sunny-overcast:before { + content: "\f00c"; +} +.wi-day-thunderstorm:before { + content: "\f010"; +} +.wi-day-windy:before { + content: "\f085"; +} +.wi-solar-eclipse:before { + content: "\f06e"; +} +.wi-hot:before { + content: "\f072"; +} +.wi-day-cloudy-high:before { + content: "\f07d"; +} +.wi-day-light-wind:before { + content: "\f0c4"; +} +.wi-night-clear:before { + content: "\f02e"; +} +.wi-night-alt-cloudy:before { + content: "\f086"; +} +.wi-night-alt-cloudy-gusts:before { + content: "\f022"; +} +.wi-night-alt-cloudy-windy:before { + content: "\f023"; +} +.wi-night-alt-hail:before { + content: "\f024"; +} +.wi-night-alt-lightning:before { + content: "\f025"; +} +.wi-night-alt-rain:before { + content: "\f028"; +} +.wi-night-alt-rain-mix:before { + content: "\f026"; +} +.wi-night-alt-rain-wind:before { + content: "\f027"; +} +.wi-night-alt-showers:before { + content: "\f029"; +} +.wi-night-alt-sleet:before { + content: "\f0b4"; +} +.wi-night-alt-sleet-storm:before { + content: "\f06a"; +} +.wi-night-alt-snow:before { + content: "\f02a"; +} +.wi-night-alt-snow-thunderstorm:before { + content: "\f06d"; +} +.wi-night-alt-snow-wind:before { + content: "\f067"; +} +.wi-night-alt-sprinkle:before { + content: "\f02b"; +} +.wi-night-alt-storm-showers:before { + content: "\f02c"; +} +.wi-night-alt-thunderstorm:before { + content: "\f02d"; +} +.wi-night-cloudy:before { + content: "\f031"; +} +.wi-night-cloudy-gusts:before { + content: "\f02f"; +} +.wi-night-cloudy-windy:before { + content: "\f030"; +} +.wi-night-fog:before { + content: "\f04a"; +} +.wi-night-hail:before { + content: "\f032"; +} +.wi-night-lightning:before { + content: "\f033"; +} +.wi-night-partly-cloudy:before { + content: "\f083"; +} +.wi-night-rain:before { + content: "\f036"; +} +.wi-night-rain-mix:before { + content: "\f034"; +} +.wi-night-rain-wind:before { + content: "\f035"; +} +.wi-night-showers:before { + content: "\f037"; +} +.wi-night-sleet:before { + content: "\f0b3"; +} +.wi-night-sleet-storm:before { + content: "\f069"; +} +.wi-night-snow:before { + content: "\f038"; +} +.wi-night-snow-thunderstorm:before { + content: "\f06c"; +} +.wi-night-snow-wind:before { + content: "\f066"; +} +.wi-night-sprinkle:before { + content: "\f039"; +} +.wi-night-storm-showers:before { + content: "\f03a"; +} +.wi-night-thunderstorm:before { + content: "\f03b"; +} +.wi-lunar-eclipse:before { + content: "\f070"; +} +.wi-stars:before { + content: "\f077"; +} +.wi-storm-showers:before { + content: "\f01d"; +} +.wi-thunderstorm:before { + content: "\f01e"; +} +.wi-night-alt-cloudy-high:before { + content: "\f07e"; +} +.wi-night-cloudy-high:before { + content: "\f080"; +} +.wi-night-alt-partly-cloudy:before { + content: "\f081"; +} +.wi-cloud:before { + content: "\f041"; +} +.wi-cloudy:before { + content: "\f013"; +} +.wi-cloudy-gusts:before { + content: "\f011"; +} +.wi-cloudy-windy:before { + content: "\f012"; +} +.wi-fog:before { + content: "\f014"; +} +.wi-hail:before { + content: "\f015"; +} +.wi-rain:before { + content: "\f019"; +} +.wi-rain-mix:before { + content: "\f017"; +} +.wi-rain-wind:before { + content: "\f018"; +} +.wi-showers:before { + content: "\f01a"; +} +.wi-sleet:before { + content: "\f0b5"; +} +.wi-snow:before { + content: "\f01b"; +} +.wi-sprinkle:before { + content: "\f01c"; +} +.wi-storm-showers:before { + content: "\f01d"; +} +.wi-thunderstorm:before { + content: "\f01e"; +} +.wi-snow-wind:before { + content: "\f064"; +} +.wi-snow:before { + content: "\f01b"; +} +.wi-smog:before { + content: "\f074"; +} +.wi-smoke:before { + content: "\f062"; +} +.wi-lightning:before { + content: "\f016"; +} +.wi-raindrops:before { + content: "\f04e"; +} +.wi-raindrop:before { + content: "\f078"; +} +.wi-dust:before { + content: "\f063"; +} +.wi-snowflake-cold:before { + content: "\f076"; +} +.wi-windy:before { + content: "\f021"; +} +.wi-strong-wind:before { + content: "\f050"; +} +.wi-sandstorm:before { + content: "\f082"; +} +.wi-earthquake:before { + content: "\f0c6"; +} +.wi-fire:before { + content: "\f0c7"; +} +.wi-flood:before { + content: "\f07c"; +} +.wi-meteor:before { + content: "\f071"; +} +.wi-tsunami:before { + content: "\f0c5"; +} +.wi-volcano:before { + content: "\f0c8"; +} +.wi-hurricane:before { + content: "\f073"; +} +.wi-tornado:before { + content: "\f056"; +} +.wi-small-craft-advisory:before { + content: "\f0cc"; +} +.wi-gale-warning:before { + content: "\f0cd"; +} +.wi-storm-warning:before { + content: "\f0ce"; +} +.wi-hurricane-warning:before { + content: "\f0cf"; +} +.wi-wind-direction:before { + content: "\f0b1"; +} +.wi-alien:before { + content: "\f075"; +} +.wi-celsius:before { + content: "\f03c"; +} +.wi-fahrenheit:before { + content: "\f045"; +} +.wi-degrees:before { + content: "\f042"; +} +.wi-thermometer:before { + content: "\f055"; +} +.wi-thermometer-exterior:before { + content: "\f053"; +} +.wi-thermometer-internal:before { + content: "\f054"; +} +.wi-cloud-down:before { + content: "\f03d"; +} +.wi-cloud-up:before { + content: "\f040"; +} +.wi-cloud-refresh:before { + content: "\f03e"; +} +.wi-horizon:before { + content: "\f047"; +} +.wi-horizon-alt:before { + content: "\f046"; +} +.wi-sunrise:before { + content: "\f051"; +} +.wi-sunset:before { + content: "\f052"; +} +.wi-moonrise:before { + content: "\f0c9"; +} +.wi-moonset:before { + content: "\f0ca"; +} +.wi-refresh:before { + content: "\f04c"; +} +.wi-refresh-alt:before { + content: "\f04b"; +} +.wi-umbrella:before { + content: "\f084"; +} +.wi-barometer:before { + content: "\f079"; +} +.wi-humidity:before { + content: "\f07a"; +} +.wi-na:before { + content: "\f07b"; +} +.wi-train:before { + content: "\f0cb"; +} +.wi-moon-new:before { + content: "\f095"; +} +.wi-moon-waxing-crescent-1:before { + content: "\f096"; +} +.wi-moon-waxing-crescent-2:before { + content: "\f097"; +} +.wi-moon-waxing-crescent-3:before { + content: "\f098"; +} +.wi-moon-waxing-crescent-4:before { + content: "\f099"; +} +.wi-moon-waxing-crescent-5:before { + content: "\f09a"; +} +.wi-moon-waxing-crescent-6:before { + content: "\f09b"; +} +.wi-moon-first-quarter:before { + content: "\f09c"; +} +.wi-moon-waxing-gibbous-1:before { + content: "\f09d"; +} +.wi-moon-waxing-gibbous-2:before { + content: "\f09e"; +} +.wi-moon-waxing-gibbous-3:before { + content: "\f09f"; +} +.wi-moon-waxing-gibbous-4:before { + content: "\f0a0"; +} +.wi-moon-waxing-gibbous-5:before { + content: "\f0a1"; +} +.wi-moon-waxing-gibbous-6:before { + content: "\f0a2"; +} +.wi-moon-full:before { + content: "\f0a3"; +} +.wi-moon-waning-gibbous-1:before { + content: "\f0a4"; +} +.wi-moon-waning-gibbous-2:before { + content: "\f0a5"; +} +.wi-moon-waning-gibbous-3:before { + content: "\f0a6"; +} +.wi-moon-waning-gibbous-4:before { + content: "\f0a7"; +} +.wi-moon-waning-gibbous-5:before { + content: "\f0a8"; +} +.wi-moon-waning-gibbous-6:before { + content: "\f0a9"; +} +.wi-moon-third-quarter:before { + content: "\f0aa"; +} +.wi-moon-waning-crescent-1:before { + content: "\f0ab"; +} +.wi-moon-waning-crescent-2:before { + content: "\f0ac"; +} +.wi-moon-waning-crescent-3:before { + content: "\f0ad"; +} +.wi-moon-waning-crescent-4:before { + content: "\f0ae"; +} +.wi-moon-waning-crescent-5:before { + content: "\f0af"; +} +.wi-moon-waning-crescent-6:before { + content: "\f0b0"; +} +.wi-moon-alt-new:before { + content: "\f0eb"; +} +.wi-moon-alt-waxing-crescent-1:before { + content: "\f0d0"; +} +.wi-moon-alt-waxing-crescent-2:before { + content: "\f0d1"; +} +.wi-moon-alt-waxing-crescent-3:before { + content: "\f0d2"; +} +.wi-moon-alt-waxing-crescent-4:before { + content: "\f0d3"; +} +.wi-moon-alt-waxing-crescent-5:before { + content: "\f0d4"; +} +.wi-moon-alt-waxing-crescent-6:before { + content: "\f0d5"; +} +.wi-moon-alt-first-quarter:before { + content: "\f0d6"; +} +.wi-moon-alt-waxing-gibbous-1:before { + content: "\f0d7"; +} +.wi-moon-alt-waxing-gibbous-2:before { + content: "\f0d8"; +} +.wi-moon-alt-waxing-gibbous-3:before { + content: "\f0d9"; +} +.wi-moon-alt-waxing-gibbous-4:before { + content: "\f0da"; +} +.wi-moon-alt-waxing-gibbous-5:before { + content: "\f0db"; +} +.wi-moon-alt-waxing-gibbous-6:before { + content: "\f0dc"; +} +.wi-moon-alt-full:before { + content: "\f0dd"; +} +.wi-moon-alt-waning-gibbous-1:before { + content: "\f0de"; +} +.wi-moon-alt-waning-gibbous-2:before { + content: "\f0df"; +} +.wi-moon-alt-waning-gibbous-3:before { + content: "\f0e0"; +} +.wi-moon-alt-waning-gibbous-4:before { + content: "\f0e1"; +} +.wi-moon-alt-waning-gibbous-5:before { + content: "\f0e2"; +} +.wi-moon-alt-waning-gibbous-6:before { + content: "\f0e3"; +} +.wi-moon-alt-third-quarter:before { + content: "\f0e4"; +} +.wi-moon-alt-waning-crescent-1:before { + content: "\f0e5"; +} +.wi-moon-alt-waning-crescent-2:before { + content: "\f0e6"; +} +.wi-moon-alt-waning-crescent-3:before { + content: "\f0e7"; +} +.wi-moon-alt-waning-crescent-4:before { + content: "\f0e8"; +} +.wi-moon-alt-waning-crescent-5:before { + content: "\f0e9"; +} +.wi-moon-alt-waning-crescent-6:before { + content: "\f0ea"; +} +.wi-moon-0:before { + content: "\f095"; +} +.wi-moon-1:before { + content: "\f096"; +} +.wi-moon-2:before { + content: "\f097"; +} +.wi-moon-3:before { + content: "\f098"; +} +.wi-moon-4:before { + content: "\f099"; +} +.wi-moon-5:before { + content: "\f09a"; +} +.wi-moon-6:before { + content: "\f09b"; +} +.wi-moon-7:before { + content: "\f09c"; +} +.wi-moon-8:before { + content: "\f09d"; +} +.wi-moon-9:before { + content: "\f09e"; +} +.wi-moon-10:before { + content: "\f09f"; +} +.wi-moon-11:before { + content: "\f0a0"; +} +.wi-moon-12:before { + content: "\f0a1"; +} +.wi-moon-13:before { + content: "\f0a2"; +} +.wi-moon-14:before { + content: "\f0a3"; +} +.wi-moon-15:before { + content: "\f0a4"; +} +.wi-moon-16:before { + content: "\f0a5"; +} +.wi-moon-17:before { + content: "\f0a6"; +} +.wi-moon-18:before { + content: "\f0a7"; +} +.wi-moon-19:before { + content: "\f0a8"; +} +.wi-moon-20:before { + content: "\f0a9"; +} +.wi-moon-21:before { + content: "\f0aa"; +} +.wi-moon-22:before { + content: "\f0ab"; +} +.wi-moon-23:before { + content: "\f0ac"; +} +.wi-moon-24:before { + content: "\f0ad"; +} +.wi-moon-25:before { + content: "\f0ae"; +} +.wi-moon-26:before { + content: "\f0af"; +} +.wi-moon-27:before { + content: "\f0b0"; +} +.wi-time-1:before { + content: "\f08a"; +} +.wi-time-2:before { + content: "\f08b"; +} +.wi-time-3:before { + content: "\f08c"; +} +.wi-time-4:before { + content: "\f08d"; +} +.wi-time-5:before { + content: "\f08e"; +} +.wi-time-6:before { + content: "\f08f"; +} +.wi-time-7:before { + content: "\f090"; +} +.wi-time-8:before { + content: "\f091"; +} +.wi-time-9:before { + content: "\f092"; +} +.wi-time-10:before { + content: "\f093"; +} +.wi-time-11:before { + content: "\f094"; +} +.wi-time-12:before { + content: "\f089"; +} +.wi-direction-up:before { + content: "\f058"; +} +.wi-direction-up-right:before { + content: "\f057"; +} +.wi-direction-right:before { + content: "\f04d"; +} +.wi-direction-down-right:before { + content: "\f088"; +} +.wi-direction-down:before { + content: "\f044"; +} +.wi-direction-down-left:before { + content: "\f043"; +} +.wi-direction-left:before { + content: "\f048"; +} +.wi-direction-up-left:before { + content: "\f087"; +} +.wi-wind-beaufort-0:before { + content: "\f0b7"; +} +.wi-wind-beaufort-1:before { + content: "\f0b8"; +} +.wi-wind-beaufort-2:before { + content: "\f0b9"; +} +.wi-wind-beaufort-3:before { + content: "\f0ba"; +} +.wi-wind-beaufort-4:before { + content: "\f0bb"; +} +.wi-wind-beaufort-5:before { + content: "\f0bc"; +} +.wi-wind-beaufort-6:before { + content: "\f0bd"; +} +.wi-wind-beaufort-7:before { + content: "\f0be"; +} +.wi-wind-beaufort-8:before { + content: "\f0bf"; +} +.wi-wind-beaufort-9:before { + content: "\f0c0"; +} +.wi-wind-beaufort-10:before { + content: "\f0c1"; +} +.wi-wind-beaufort-11:before { + content: "\f0c2"; +} +.wi-wind-beaufort-12:before { + content: "\f0c3"; +} +.wi-yahoo-0:before { + content: "\f056"; +} +.wi-yahoo-1:before { + content: "\f00e"; +} +.wi-yahoo-2:before { + content: "\f073"; +} +.wi-yahoo-3:before { + content: "\f01e"; +} +.wi-yahoo-4:before { + content: "\f01e"; +} +.wi-yahoo-5:before { + content: "\f017"; +} +.wi-yahoo-6:before { + content: "\f017"; +} +.wi-yahoo-7:before { + content: "\f017"; +} +.wi-yahoo-8:before { + content: "\f015"; +} +.wi-yahoo-9:before { + content: "\f01a"; +} +.wi-yahoo-10:before { + content: "\f015"; +} +.wi-yahoo-11:before { + content: "\f01a"; +} +.wi-yahoo-12:before { + content: "\f01a"; +} +.wi-yahoo-13:before { + content: "\f01b"; +} +.wi-yahoo-14:before { + content: "\f00a"; +} +.wi-yahoo-15:before { + content: "\f064"; +} +.wi-yahoo-16:before { + content: "\f01b"; +} +.wi-yahoo-17:before { + content: "\f015"; +} +.wi-yahoo-18:before { + content: "\f017"; +} +.wi-yahoo-19:before { + content: "\f063"; +} +.wi-yahoo-20:before { + content: "\f014"; +} +.wi-yahoo-21:before { + content: "\f021"; +} +.wi-yahoo-22:before { + content: "\f062"; +} +.wi-yahoo-23:before { + content: "\f050"; +} +.wi-yahoo-24:before { + content: "\f050"; +} +.wi-yahoo-25:before { + content: "\f076"; +} +.wi-yahoo-26:before { + content: "\f013"; +} +.wi-yahoo-27:before { + content: "\f031"; +} +.wi-yahoo-28:before { + content: "\f002"; +} +.wi-yahoo-29:before { + content: "\f031"; +} +.wi-yahoo-30:before { + content: "\f002"; +} +.wi-yahoo-31:before { + content: "\f02e"; +} +.wi-yahoo-32:before { + content: "\f00d"; +} +.wi-yahoo-33:before { + content: "\f083"; +} +.wi-yahoo-34:before { + content: "\f00c"; +} +.wi-yahoo-35:before { + content: "\f017"; +} +.wi-yahoo-36:before { + content: "\f072"; +} +.wi-yahoo-37:before { + content: "\f00e"; +} +.wi-yahoo-38:before { + content: "\f00e"; +} +.wi-yahoo-39:before { + content: "\f00e"; +} +.wi-yahoo-40:before { + content: "\f01a"; +} +.wi-yahoo-41:before { + content: "\f064"; +} +.wi-yahoo-42:before { + content: "\f01b"; +} +.wi-yahoo-43:before { + content: "\f064"; +} +.wi-yahoo-44:before { + content: "\f00c"; +} +.wi-yahoo-45:before { + content: "\f00e"; +} +.wi-yahoo-46:before { + content: "\f01b"; +} +.wi-yahoo-47:before { + content: "\f00e"; +} +.wi-yahoo-3200:before { + content: "\f077"; +} +.wi-forecast-io-clear-day:before { + content: "\f00d"; +} +.wi-forecast-io-clear-night:before { + content: "\f02e"; +} +.wi-forecast-io-rain:before { + content: "\f019"; +} +.wi-forecast-io-snow:before { + content: "\f01b"; +} +.wi-forecast-io-sleet:before { + content: "\f0b5"; +} +.wi-forecast-io-wind:before { + content: "\f050"; +} +.wi-forecast-io-fog:before { + content: "\f014"; +} +.wi-forecast-io-cloudy:before { + content: "\f013"; +} +.wi-forecast-io-partly-cloudy-day:before { + content: "\f002"; +} +.wi-forecast-io-partly-cloudy-night:before { + content: "\f031"; +} +.wi-forecast-io-hail:before { + content: "\f015"; +} +.wi-forecast-io-thunderstorm:before { + content: "\f01e"; +} +.wi-forecast-io-tornado:before { + content: "\f056"; +} +.wi-wmo4680-0:before, +.wi-wmo4680-00:before { + content: "\f055"; +} +.wi-wmo4680-1:before, +.wi-wmo4680-01:before { + content: "\f013"; +} +.wi-wmo4680-2:before, +.wi-wmo4680-02:before { + content: "\f055"; +} +.wi-wmo4680-3:before, +.wi-wmo4680-03:before { + content: "\f013"; +} +.wi-wmo4680-4:before, +.wi-wmo4680-04:before { + content: "\f014"; +} +.wi-wmo4680-5:before, +.wi-wmo4680-05:before { + content: "\f014"; +} +.wi-wmo4680-10:before { + content: "\f014"; +} +.wi-wmo4680-11:before { + content: "\f014"; +} +.wi-wmo4680-12:before { + content: "\f016"; +} +.wi-wmo4680-18:before { + content: "\f050"; +} +.wi-wmo4680-20:before { + content: "\f014"; +} +.wi-wmo4680-21:before { + content: "\f017"; +} +.wi-wmo4680-22:before { + content: "\f017"; +} +.wi-wmo4680-23:before { + content: "\f019"; +} +.wi-wmo4680-24:before { + content: "\f01b"; +} +.wi-wmo4680-25:before { + content: "\f015"; +} +.wi-wmo4680-26:before { + content: "\f01e"; +} +.wi-wmo4680-27:before { + content: "\f063"; +} +.wi-wmo4680-28:before { + content: "\f063"; +} +.wi-wmo4680-29:before { + content: "\f063"; +} +.wi-wmo4680-30:before { + content: "\f014"; +} +.wi-wmo4680-31:before { + content: "\f014"; +} +.wi-wmo4680-32:before { + content: "\f014"; +} +.wi-wmo4680-33:before { + content: "\f014"; +} +.wi-wmo4680-34:before { + content: "\f014"; +} +.wi-wmo4680-35:before { + content: "\f014"; +} +.wi-wmo4680-40:before { + content: "\f017"; +} +.wi-wmo4680-41:before { + content: "\f01c"; +} +.wi-wmo4680-42:before { + content: "\f019"; +} +.wi-wmo4680-43:before { + content: "\f01c"; +} +.wi-wmo4680-44:before { + content: "\f019"; +} +.wi-wmo4680-45:before { + content: "\f015"; +} +.wi-wmo4680-46:before { + content: "\f015"; +} +.wi-wmo4680-47:before { + content: "\f01b"; +} +.wi-wmo4680-48:before { + content: "\f01b"; +} +.wi-wmo4680-50:before { + content: "\f01c"; +} +.wi-wmo4680-51:before { + content: "\f01c"; +} +.wi-wmo4680-52:before { + content: "\f019"; +} +.wi-wmo4680-53:before { + content: "\f019"; +} +.wi-wmo4680-54:before { + content: "\f076"; +} +.wi-wmo4680-55:before { + content: "\f076"; +} +.wi-wmo4680-56:before { + content: "\f076"; +} +.wi-wmo4680-57:before { + content: "\f01c"; +} +.wi-wmo4680-58:before { + content: "\f019"; +} +.wi-wmo4680-60:before { + content: "\f01c"; +} +.wi-wmo4680-61:before { + content: "\f01c"; +} +.wi-wmo4680-62:before { + content: "\f019"; +} +.wi-wmo4680-63:before { + content: "\f019"; +} +.wi-wmo4680-64:before { + content: "\f015"; +} +.wi-wmo4680-65:before { + content: "\f015"; +} +.wi-wmo4680-66:before { + content: "\f015"; +} +.wi-wmo4680-67:before { + content: "\f017"; +} +.wi-wmo4680-68:before { + content: "\f017"; +} +.wi-wmo4680-70:before { + content: "\f01b"; +} +.wi-wmo4680-71:before { + content: "\f01b"; +} +.wi-wmo4680-72:before { + content: "\f01b"; +} +.wi-wmo4680-73:before { + content: "\f01b"; +} +.wi-wmo4680-74:before { + content: "\f076"; +} +.wi-wmo4680-75:before { + content: "\f076"; +} +.wi-wmo4680-76:before { + content: "\f076"; +} +.wi-wmo4680-77:before { + content: "\f01b"; +} +.wi-wmo4680-78:before { + content: "\f076"; +} +.wi-wmo4680-80:before { + content: "\f019"; +} +.wi-wmo4680-81:before { + content: "\f01c"; +} +.wi-wmo4680-82:before { + content: "\f019"; +} +.wi-wmo4680-83:before { + content: "\f019"; +} +.wi-wmo4680-84:before { + content: "\f01d"; +} +.wi-wmo4680-85:before { + content: "\f017"; +} +.wi-wmo4680-86:before { + content: "\f017"; +} +.wi-wmo4680-87:before { + content: "\f017"; +} +.wi-wmo4680-89:before { + content: "\f015"; +} +.wi-wmo4680-90:before { + content: "\f016"; +} +.wi-wmo4680-91:before { + content: "\f01d"; +} +.wi-wmo4680-92:before { + content: "\f01e"; +} +.wi-wmo4680-93:before { + content: "\f01e"; +} +.wi-wmo4680-94:before { + content: "\f016"; +} +.wi-wmo4680-95:before { + content: "\f01e"; +} +.wi-wmo4680-96:before { + content: "\f01e"; +} +.wi-wmo4680-99:before { + content: "\f056"; +} +.wi-owm-200:before { + content: "\f01e"; +} +.wi-owm-201:before { + content: "\f01e"; +} +.wi-owm-202:before { + content: "\f01e"; +} +.wi-owm-210:before { + content: "\f016"; +} +.wi-owm-211:before { + content: "\f016"; +} +.wi-owm-212:before { + content: "\f016"; +} +.wi-owm-221:before { + content: "\f016"; +} +.wi-owm-230:before { + content: "\f01e"; +} +.wi-owm-231:before { + content: "\f01e"; +} +.wi-owm-232:before { + content: "\f01e"; +} +.wi-owm-300:before { + content: "\f01c"; +} +.wi-owm-301:before { + content: "\f01c"; +} +.wi-owm-302:before { + content: "\f019"; +} +.wi-owm-310:before { + content: "\f017"; +} +.wi-owm-311:before { + content: "\f019"; +} +.wi-owm-312:before { + content: "\f019"; +} +.wi-owm-313:before { + content: "\f01a"; +} +.wi-owm-314:before { + content: "\f019"; +} +.wi-owm-321:before { + content: "\f01c"; +} +.wi-owm-500:before { + content: "\f01c"; +} +.wi-owm-501:before { + content: "\f019"; +} +.wi-owm-502:before { + content: "\f019"; +} +.wi-owm-503:before { + content: "\f019"; +} +.wi-owm-504:before { + content: "\f019"; +} +.wi-owm-511:before { + content: "\f017"; +} +.wi-owm-520:before { + content: "\f01a"; +} +.wi-owm-521:before { + content: "\f01a"; +} +.wi-owm-522:before { + content: "\f01a"; +} +.wi-owm-531:before { + content: "\f01d"; +} +.wi-owm-600:before { + content: "\f01b"; +} +.wi-owm-601:before { + content: "\f01b"; +} +.wi-owm-602:before { + content: "\f0b5"; +} +.wi-owm-611:before { + content: "\f017"; +} +.wi-owm-612:before { + content: "\f017"; +} +.wi-owm-615:before { + content: "\f017"; +} +.wi-owm-616:before { + content: "\f017"; +} +.wi-owm-620:before { + content: "\f017"; +} +.wi-owm-621:before { + content: "\f01b"; +} +.wi-owm-622:before { + content: "\f01b"; +} +.wi-owm-701:before { + content: "\f014"; +} +.wi-owm-711:before { + content: "\f062"; +} +.wi-owm-721:before { + content: "\f0b6"; +} +.wi-owm-731:before { + content: "\f063"; +} +.wi-owm-741:before { + content: "\f014"; +} +.wi-owm-761:before { + content: "\f063"; +} +.wi-owm-762:before { + content: "\f063"; +} +.wi-owm-771:before { + content: "\f011"; +} +.wi-owm-781:before { + content: "\f056"; +} +.wi-owm-800:before { + content: "\f00d"; +} +.wi-owm-801:before { + content: "\f041"; +} +.wi-owm-802:before { + content: "\f041"; +} +.wi-owm-803:before { + content: "\f013"; +} +.wi-owm-804:before { + content: "\f013"; +} +.wi-owm-900:before { + content: "\f056"; +} +.wi-owm-901:before { + content: "\f01d"; +} +.wi-owm-902:before { + content: "\f073"; +} +.wi-owm-903:before { + content: "\f076"; +} +.wi-owm-904:before { + content: "\f072"; +} +.wi-owm-905:before { + content: "\f021"; +} +.wi-owm-906:before { + content: "\f015"; +} +.wi-owm-957:before { + content: "\f050"; +} +.wi-owm-day-200:before { + content: "\f010"; +} +.wi-owm-day-201:before { + content: "\f010"; +} +.wi-owm-day-202:before { + content: "\f010"; +} +.wi-owm-day-210:before { + content: "\f005"; +} +.wi-owm-day-211:before { + content: "\f005"; +} +.wi-owm-day-212:before { + content: "\f005"; +} +.wi-owm-day-221:before { + content: "\f005"; +} +.wi-owm-day-230:before { + content: "\f010"; +} +.wi-owm-day-231:before { + content: "\f010"; +} +.wi-owm-day-232:before { + content: "\f010"; +} +.wi-owm-day-300:before { + content: "\f00b"; +} +.wi-owm-day-301:before { + content: "\f00b"; +} +.wi-owm-day-302:before { + content: "\f008"; +} +.wi-owm-day-310:before { + content: "\f008"; +} +.wi-owm-day-311:before { + content: "\f008"; +} +.wi-owm-day-312:before { + content: "\f008"; +} +.wi-owm-day-313:before { + content: "\f008"; +} +.wi-owm-day-314:before { + content: "\f008"; +} +.wi-owm-day-321:before { + content: "\f00b"; +} +.wi-owm-day-500:before { + content: "\f00b"; +} +.wi-owm-day-501:before { + content: "\f008"; +} +.wi-owm-day-502:before { + content: "\f008"; +} +.wi-owm-day-503:before { + content: "\f008"; +} +.wi-owm-day-504:before { + content: "\f008"; +} +.wi-owm-day-511:before { + content: "\f006"; +} +.wi-owm-day-520:before { + content: "\f009"; +} +.wi-owm-day-521:before { + content: "\f009"; +} +.wi-owm-day-522:before { + content: "\f009"; +} +.wi-owm-day-531:before { + content: "\f00e"; +} +.wi-owm-day-600:before { + content: "\f00a"; +} +.wi-owm-day-601:before { + content: "\f0b2"; +} +.wi-owm-day-602:before { + content: "\f00a"; +} +.wi-owm-day-611:before { + content: "\f006"; +} +.wi-owm-day-612:before { + content: "\f006"; +} +.wi-owm-day-615:before { + content: "\f006"; +} +.wi-owm-day-616:before { + content: "\f006"; +} +.wi-owm-day-620:before { + content: "\f006"; +} +.wi-owm-day-621:before { + content: "\f00a"; +} +.wi-owm-day-622:before { + content: "\f00a"; +} +.wi-owm-day-701:before { + content: "\f003"; +} +.wi-owm-day-711:before { + content: "\f062"; +} +.wi-owm-day-721:before { + content: "\f0b6"; +} +.wi-owm-day-731:before { + content: "\f063"; +} +.wi-owm-day-741:before { + content: "\f003"; +} +.wi-owm-day-761:before { + content: "\f063"; +} +.wi-owm-day-762:before { + content: "\f063"; +} +.wi-owm-day-781:before { + content: "\f056"; +} +.wi-owm-day-800:before { + content: "\f00d"; +} +.wi-owm-day-801:before { + content: "\f002"; +} +.wi-owm-day-802:before { + content: "\f002"; +} +.wi-owm-day-803:before { + content: "\f013"; +} +.wi-owm-day-804:before { + content: "\f013"; +} +.wi-owm-day-900:before { + content: "\f056"; +} +.wi-owm-day-902:before { + content: "\f073"; +} +.wi-owm-day-903:before { + content: "\f076"; +} +.wi-owm-day-904:before { + content: "\f072"; +} +.wi-owm-day-906:before { + content: "\f004"; +} +.wi-owm-day-957:before { + content: "\f050"; +} +.wi-owm-night-200:before { + content: "\f02d"; +} +.wi-owm-night-201:before { + content: "\f02d"; +} +.wi-owm-night-202:before { + content: "\f02d"; +} +.wi-owm-night-210:before { + content: "\f025"; +} +.wi-owm-night-211:before { + content: "\f025"; +} +.wi-owm-night-212:before { + content: "\f025"; +} +.wi-owm-night-221:before { + content: "\f025"; +} +.wi-owm-night-230:before { + content: "\f02d"; +} +.wi-owm-night-231:before { + content: "\f02d"; +} +.wi-owm-night-232:before { + content: "\f02d"; +} +.wi-owm-night-300:before { + content: "\f02b"; +} +.wi-owm-night-301:before { + content: "\f02b"; +} +.wi-owm-night-302:before { + content: "\f028"; +} +.wi-owm-night-310:before { + content: "\f028"; +} +.wi-owm-night-311:before { + content: "\f028"; +} +.wi-owm-night-312:before { + content: "\f028"; +} +.wi-owm-night-313:before { + content: "\f028"; +} +.wi-owm-night-314:before { + content: "\f028"; +} +.wi-owm-night-321:before { + content: "\f02b"; +} +.wi-owm-night-500:before { + content: "\f02b"; +} +.wi-owm-night-501:before { + content: "\f028"; +} +.wi-owm-night-502:before { + content: "\f028"; +} +.wi-owm-night-503:before { + content: "\f028"; +} +.wi-owm-night-504:before { + content: "\f028"; +} +.wi-owm-night-511:before { + content: "\f026"; +} +.wi-owm-night-520:before { + content: "\f029"; +} +.wi-owm-night-521:before { + content: "\f029"; +} +.wi-owm-night-522:before { + content: "\f029"; +} +.wi-owm-night-531:before { + content: "\f02c"; +} +.wi-owm-night-600:before { + content: "\f02a"; +} +.wi-owm-night-601:before { + content: "\f0b4"; +} +.wi-owm-night-602:before { + content: "\f02a"; +} +.wi-owm-night-611:before { + content: "\f026"; +} +.wi-owm-night-612:before { + content: "\f026"; +} +.wi-owm-night-615:before { + content: "\f026"; +} +.wi-owm-night-616:before { + content: "\f026"; +} +.wi-owm-night-620:before { + content: "\f026"; +} +.wi-owm-night-621:before { + content: "\f02a"; +} +.wi-owm-night-622:before { + content: "\f02a"; +} +.wi-owm-night-701:before { + content: "\f04a"; +} +.wi-owm-night-711:before { + content: "\f062"; +} +.wi-owm-night-721:before { + content: "\f0b6"; +} +.wi-owm-night-731:before { + content: "\f063"; +} +.wi-owm-night-741:before { + content: "\f04a"; +} +.wi-owm-night-761:before { + content: "\f063"; +} +.wi-owm-night-762:before { + content: "\f063"; +} +.wi-owm-night-781:before { + content: "\f056"; +} +.wi-owm-night-800:before { + content: "\f02e"; +} +.wi-owm-night-801:before { + content: "\f081"; +} +.wi-owm-night-802:before { + content: "\f086"; +} +.wi-owm-night-803:before { + content: "\f013"; +} +.wi-owm-night-804:before { + content: "\f013"; +} +.wi-owm-night-900:before { + content: "\f056"; +} +.wi-owm-night-902:before { + content: "\f073"; +} +.wi-owm-night-903:before { + content: "\f076"; +} +.wi-owm-night-904:before { + content: "\f072"; +} +.wi-owm-night-906:before { + content: "\f024"; +} +.wi-owm-night-957:before { + content: "\f050"; +} +.wi-wu-chanceflurries:before { + content: "\f064"; +} +.wi-wu-chancerain:before { + content: "\f019"; +} +.wi-wu-chancesleat:before { + content: "\f0b5"; +} +.wi-wu-chancesnow:before { + content: "\f01b"; +} +.wi-wu-chancetstorms:before { + content: "\f01e"; +} +.wi-wu-clear:before { + content: "\f00d"; +} +.wi-wu-cloudy:before { + content: "\f002"; +} +.wi-wu-flurries:before { + content: "\f064"; +} +.wi-wu-hazy:before { + content: "\f0b6"; +} +.wi-wu-mostlycloudy:before { + content: "\f002"; +} +.wi-wu-mostlysunny:before { + content: "\f00d"; +} +.wi-wu-partlycloudy:before { + content: "\f002"; +} +.wi-wu-partlysunny:before { + content: "\f00d"; +} +.wi-wu-rain:before { + content: "\f01a"; +} +.wi-wu-sleat:before { + content: "\f0b5"; +} +.wi-wu-snow:before { + content: "\f01b"; +} +.wi-wu-sunny:before { + content: "\f00d"; +} +.wi-wu-tstorms:before { + content: "\f01e"; +} +.wi-wu-unknown:before { + content: "\f00d"; +} diff --git a/src/utils/cached-fetch.js b/src/utils/cached-fetch.js new file mode 100644 index 000000000..88372d53e --- /dev/null +++ b/src/utils/cached-fetch.js @@ -0,0 +1,13 @@ +import cache from "memory-cache"; + +export default async function cachedFetch(url, duration) { + const cached = cache.get(url); + + if (cached) { + return cached; + } else { + const data = await fetch(url).then((res) => res.json()); + cache.put(url, data, duration * 1000 * 60); + return data; + } +} diff --git a/src/utils/condition-map.js b/src/utils/condition-map.js new file mode 100644 index 000000000..8ab5f25bd --- /dev/null +++ b/src/utils/condition-map.js @@ -0,0 +1,356 @@ +import * as Icons from "react-icons/wi"; + +const conditions = [ + { + code: 1000, + icon: { + day: Icons.WiDaySunny, + night: Icons.WiNightClear, + }, + }, + { + code: 1003, + icon: { + day: Icons.WiDayCloudy, + night: Icons.WiNightPartlyCloudy, + }, + }, + { + code: 1006, + icon: { + day: Icons.WiDayCloudy, + night: Icons.WiNightCloudy, + }, + }, + { + code: 1009, + icon: { + day: Icons.WiDayCloudy, + night: Icons.WiNightCloudy, + }, + }, + { + code: 1030, + icon: { + day: Icons.WiDayFog, + night: Icons.WiNightFog, + }, + }, + { + code: 1063, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1066, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1069, + icon: { + day: Icons.WiDayRainMix, + night: Icons.WiNightRainMix, + }, + }, + { + code: 1072, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightSleet, + }, + }, + { + code: 1087, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightThunderstorm, + }, + }, + { + code: 1114, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1117, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1135, + icon: { + day: Icons.WiDayFog, + night: Icons.WiNightFog, + }, + }, + { + code: 1147, + icon: { + day: Icons.WiDayFog, + night: Icons.WiNightFog, + }, + }, + { + code: 1150, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1153, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1168, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightSleet, + }, + }, + { + code: 1171, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightSleet, + }, + }, + { + code: 1180, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1183, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1186, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1189, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1192, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1195, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1198, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightSleet, + }, + }, + { + code: 1201, + icon: { + day: Icons.WiDaySleet, + night: Icons.WiNightSleet, + }, + }, + { + code: 1204, + icon: { + day: Icons.WiDayRainMix, + night: Icons.WiNightRainMix, + }, + }, + { + code: 1207, + icon: { + day: Icons.WiDayRainMix, + night: Icons.WiNightRainMix, + }, + }, + { + code: 1210, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1213, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1216, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1219, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1222, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1225, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1237, + icon: { + day: Icons.WiDayHail, + night: Icons.WiNightHail, + }, + }, + { + code: 1240, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1243, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1246, + icon: { + day: Icons.WiDayRain, + night: Icons.WiNightRain, + }, + }, + { + code: 1249, + icon: { + day: Icons.WiDayRainMix, + night: Icons.WiNightRainMix, + }, + }, + { + code: 1252, + icon: { + day: Icons.WiDayRainMix, + night: Icons.WiNightRainMix, + }, + }, + { + code: 1255, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1258, + icon: { + day: Icons.WiDaySnow, + night: Icons.WiNightSnow, + }, + }, + { + code: 1261, + icon: { + day: Icons.WiDayHail, + night: Icons.WiNightHail, + }, + }, + { + code: 1264, + icon: { + day: Icons.WiDayHail, + night: Icons.WiNightHail, + }, + }, + { + code: 1273, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightThunderstorm, + }, + }, + { + code: 1276, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightThunderstorm, + }, + }, + { + code: 1279, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightThunderstorm, + }, + }, + { + code: 1282, + icon: { + day: Icons.WiDayThunderstorm, + night: Icons.WiNightThunderstorm, + }, + }, +]; + +export default function mapIcon(weatherStatusCode, timeOfDay) { + const mapping = conditions.find( + (condition) => condition.code === weatherStatusCode + ); + + if (mapping) { + if (timeOfDay === "day") { + return mapping.icon.day; + } else if (timeOfDay === "night") { + return mapping.icon.night; + } + } + + return Icons.WiDaySunny; +} diff --git a/src/utils/config.js b/src/utils/config.js new file mode 100644 index 000000000..7fe9dc3ea --- /dev/null +++ b/src/utils/config.js @@ -0,0 +1,13 @@ +import { join } from "path"; +import { existsSync, copyFile } from "fs"; + +export default function checkAndCopyConfig(config) { + const configYaml = join(process.cwd(), "config", config); + if (!existsSync(configYaml)) { + const configSkeleton = join(process.cwd(), "src", "skeleton", config); + copyFile(configSkeleton, configYaml, (err) => { + if (err) throw err; + console.info("%s was copied to the config folder", config); + }); + } +} diff --git a/src/utils/docker.js b/src/utils/docker.js new file mode 100644 index 000000000..9d2d46466 --- /dev/null +++ b/src/utils/docker.js @@ -0,0 +1,30 @@ +import yaml from "js-yaml"; +import path from "path"; +import { promises as fs } from "fs"; +import checkAndCopyConfig from "utils/config"; + +export default async function getDockerArguments(server) { + checkAndCopyConfig("docker.yaml"); + + const configFile = path.join(process.cwd(), "config", "docker.yaml"); + const configData = await fs.readFile(configFile, "utf8"); + const servers = yaml.load(configData); + + if (!server) { + if (process.platform !== "win32" && process.platform !== "darwin") { + return { socketPath: "/var/run/docker.sock" }; + } else { + return { host: "127.0.0.1" }; + } + } else if (servers[server]) { + if (servers[server].socket) { + return { socketPath: servers[server].socket }; + } else if (servers[server].host) { + return { host: servers[server].host, port: servers[server].port || null }; + } else { + return servers[server]; + } + } else { + return null; + } +} diff --git a/src/utils/stats-helpers.js b/src/utils/stats-helpers.js new file mode 100644 index 000000000..4b21b4cd9 --- /dev/null +++ b/src/utils/stats-helpers.js @@ -0,0 +1,26 @@ +export function calculateCPUPercent(stats) { + let cpuPercent = 0.0; + const cpuDelta = + stats.cpu_stats.cpu_usage.total_usage - + stats.precpu_stats.cpu_usage.total_usage; + const systemDelta = + stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage; + + if (systemDelta > 0.0 && cpuDelta > 0.0) { + cpuPercent = (cpuDelta / systemDelta) * stats.cpu_stats.online_cpus * 100.0; + } + + return Math.round(cpuPercent * 10) / 10; +} + +export function formatBytes(bytes, decimals = 2) { + if (bytes === 0) return "0 Bytes"; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; +} diff --git a/src/utils/theme-context.js b/src/utils/theme-context.js new file mode 100644 index 000000000..49761633c --- /dev/null +++ b/src/utils/theme-context.js @@ -0,0 +1,47 @@ +import { createContext, useState, useEffect } from "react"; + +const getInitialTheme = () => { + if (typeof window !== "undefined" && window.localStorage) { + const storedPrefs = window.localStorage.getItem("color-theme"); + if (typeof storedPrefs === "string") { + return storedPrefs; + } + + const userMedia = window.matchMedia("(prefers-color-scheme: dark)"); + if (userMedia.matches) { + return "dark"; + } + } + + return "light"; // light theme as the default; +}; + +export const ThemeContext = createContext(); + +export const ThemeProvider = ({ initialTheme, children }) => { + const [theme, setTheme] = useState(getInitialTheme); + + const rawSetTheme = (rawTheme) => { + const root = window.document.documentElement; + const isDark = rawTheme === "dark"; + + root.classList.remove(isDark ? "light" : "dark"); + root.classList.add(rawTheme); + + localStorage.setItem("color-theme", rawTheme); + }; + + if (initialTheme) { + rawSetTheme(initialTheme); + } + + useEffect(() => { + rawSetTheme(theme); + }, [theme]); + + return ( + + {children} + + ); +}; diff --git a/styles/Home.module.css b/styles/Home.module.css deleted file mode 100644 index bd50f42ff..000000000 --- a/styles/Home.module.css +++ /dev/null @@ -1,129 +0,0 @@ -.container { - padding: 0 2rem; -} - -.main { - min-height: 100vh; - padding: 4rem 0; - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.footer { - display: flex; - flex: 1; - padding: 2rem 0; - border-top: 1px solid #eaeaea; - justify-content: center; - align-items: center; -} - -.footer a { - display: flex; - justify-content: center; - align-items: center; - flex-grow: 1; -} - -.title a { - color: #0070f3; - text-decoration: none; -} - -.title a:hover, -.title a:focus, -.title a:active { - text-decoration: underline; -} - -.title { - margin: 0; - line-height: 1.15; - font-size: 4rem; -} - -.title, -.description { - text-align: center; -} - -.description { - margin: 4rem 0; - line-height: 1.5; - font-size: 1.5rem; -} - -.code { - background: #fafafa; - border-radius: 5px; - padding: 0.75rem; - font-size: 1.1rem; - font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, - Bitstream Vera Sans Mono, Courier New, monospace; -} - -.grid { - display: flex; - align-items: center; - justify-content: center; - flex-wrap: wrap; - max-width: 800px; -} - -.card { - margin: 1rem; - padding: 1.5rem; - text-align: left; - color: inherit; - text-decoration: none; - border: 1px solid #eaeaea; - border-radius: 10px; - transition: color 0.15s ease, border-color 0.15s ease; - max-width: 300px; -} - -.card:hover, -.card:focus, -.card:active { - color: #0070f3; - border-color: #0070f3; -} - -.card h2 { - margin: 0 0 1rem 0; - font-size: 1.5rem; -} - -.card p { - margin: 0; - font-size: 1.25rem; - line-height: 1.5; -} - -.logo { - height: 1em; - margin-left: 0.5rem; -} - -@media (max-width: 600px) { - .grid { - width: 100%; - flex-direction: column; - } -} - -@media (prefers-color-scheme: dark) { - .card, - .footer { - border-color: #222; - } - .code { - background: #111; - } - .logo img { - filter: invert(1); - } -} diff --git a/styles/globals.css b/styles/globals.css deleted file mode 100644 index 4f1842163..000000000 --- a/styles/globals.css +++ /dev/null @@ -1,26 +0,0 @@ -html, -body { - padding: 0; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, - Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -} - -a { - color: inherit; - text-decoration: none; -} - -* { - box-sizing: border-box; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } - body { - color: white; - background: black; - } -} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 000000000..b81ca6d25 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,16 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: "class", + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx}", + "./src/components/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + colors: ({ colors }) => ({ + theme: colors.slate, + }), + }, + }, + plugins: [require("@tailwindcss/forms")], +};