chore(ui): switch to vite

This commit is contained in:
Guanran Wang 2024-10-26 15:27:02 +08:00
parent 2254ffb1d0
commit 3b6cca1b08
Signed by: nyancat
GPG key ID: 91F97D9ED12639CF
18 changed files with 1531 additions and 141 deletions

View file

@ -20,12 +20,16 @@
inputs.flake-utils.lib.eachDefaultSystem (
system:
let
inherit (pkgs) lib;
pkgs = inputs.nixpkgs.legacyPackages.${system};
treefmtEval = inputs.treefmt-nix.lib.evalModule pkgs ./treefmt.nix;
in
{
### nix {run,shell,build}
packages.default = pkgs.callPackage ./package.nix { };
packages = rec {
default = lib.warn "the 'default' package is now deprecated, please use the 'api' package instead." api;
inherit ((pkgs.callPackage ./package.nix { })) api ui;
};
### nix fmt
formatter = treefmtEval.config.build.wrapper;
@ -38,7 +42,8 @@
nativeBuildInputs = with pkgs; [
go
nodePackages.prettier
nodePackages.tailwindcss
nodejs
pnpm
];
};
}

View file

@ -1,16 +1,61 @@
{ lib, buildGoModule }:
{
lib,
buildGoModule,
nodejs,
pnpm,
stdenv,
}:
{
api = buildGoModule {
pname = "ip-checker-api";
version = "dev";
buildGoModule {
pname = "ip-checker";
version = "dev";
src = ./api;
src = ./api;
vendorHash = "sha256-VvZcnTEgPXlAYEf2+2WZ2xlU4TWhTz+dBiA4j76GSvM=";
vendorHash = "sha256-VvZcnTEgPXlAYEf2+2WZ2xlU4TWhTz+dBiA4j76GSvM=";
meta = {
homepage = "https://git.ny4.dev/nyancat/ip-checker";
license = lib.licenses.mit;
mainProgram = "ip-checker";
meta = {
homepage = "https://git.ny4.dev/nyancat/ip-checker";
license = lib.licenses.mit;
mainProgram = "ip-checker";
};
};
ui = stdenv.mkDerivation (finalAttrs: {
pname = "ip-checker-ui";
version = "dev";
src = ./ui;
nativeBuildInputs = [
nodejs
pnpm.configHook
];
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pname version src;
hash = "sha256-ucTUmKTeyfTSgWOcLEtf0n9VhzUfHGm41YCYLDxskh0=";
};
buildPhase = ''
runHook preBuild
pnpm build
runHook postBuild
'';
installPhase = ''
runHook preInstall
cp -r ./dist $out
runHook postInstall
'';
meta = {
homepage = "https://git.ny4.dev/nyancat/ip-checker";
license = lib.licenses.mit;
};
});
}

View file

@ -1,14 +1,15 @@
{
projectRootFile = "flake.nix";
### nix
programs.deadnix.enable = true;
programs.statix.enable = true;
programs.nixfmt.enable = true;
programs = {
deadnix.enable = true;
gofmt.enable = true;
nixfmt.enable = true;
prettier.enable = true;
statix.enable = true;
};
### go
programs.gofmt.enable = true;
### misc
programs.prettier.enable = true;
settings.formatter.prettier.excludes = [
"ui/pnpm-lock.yaml"
];
}

View file

@ -1,12 +0,0 @@
{
"compilerOptions": {
"target": "es2016",
"lib": ["es2020", "dom"],
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"rootDir": "./ui"
}
}

24
ui/.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View file

@ -1,3 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Fira+Code:wght@500&display=swap");*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }
/*! tailwindcss v3.4.12 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:Fira Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}.table-cell{border-width:2px;--tw-border-opacity:1;border-color:rgb(196 181 253/var(--tw-border-opacity));padding:.5rem 1.25rem}@media (prefers-color-scheme:dark){.table-cell{--tw-border-opacity:1;border-color:rgb(51 65 85/var(--tw-border-opacity))}}.absolute{position:absolute}.inset-x-0{left:0;right:0}.bottom-0{bottom:0}.flex{display:flex}.table{display:table}.table-cell{display:table-cell}.min-h-screen{min-height:100vh}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.overflow-x-auto{overflow-x:auto}.bg-violet-100{--tw-bg-opacity:1;background-color:rgb(237 233 254/var(--tw-bg-opacity))}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.font-mono{font-family:Fira Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity))}.text-violet-950{--tw-text-opacity:1;color:rgb(46 16 101/var(--tw-text-opacity))}.visited\:text-violet-500:visited{color:#8b5cf6}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}@media (min-width:768px){.md\:text-base{font-size:1rem;line-height:1.5rem}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (prefers-color-scheme:dark){.dark\:bg-slate-900{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity))}.dark\:text-slate-100{--tw-text-opacity:1;color:rgb(241 245 249/var(--tw-text-opacity))}.dark\:text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity))}}

View file

@ -5,8 +5,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Ny4 IP Checker" />
<title>What is my IP?</title>
<link href="generated.css" rel="stylesheet" />
<script src="script.js" defer></script>
<link rel="icon" href="favicon.svg" />
<link rel="stylesheet" href="/src/style.css" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@500&display=swap"
/>
<script type="module" src="/src/main.ts"></script>
<link rel="dns-prefetch" href="https://fonts.googleapis.com/" />
<link rel="dns-prefetch" href="https://fonts.gstatic.com/" />

20
ui/package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "ip-checker",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"typescript": "~5.6.2",
"vite": "^5.4.9"
},
"dependencies": {
"autoprefixer": "^10.4.20",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.14"
}
}

1372
ui/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

6
ui/postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

1
ui/public/favicon.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,97 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
function main() {
return __awaiter(this, void 0, void 0, function* () {
const [ny4, ipsb, speedtestcn, ipapiis] = yield Promise.allSettled([
fetchAsync("https://ip.ny4.dev/api/v1/ip"),
fetchAsync("https://api.ip.sb/geoip"),
fetchAsync("https://api-v3.speedtest.cn/ip"),
fetchAsync("https://api.ipapi.is/"),
]);
if (ny4.status === "fulfilled") {
updateDom("check-ny4-ip", ny4.value.ip);
updateDom("check-ny4-location", [
ny4.value.city,
ny4.value.region,
ny4.value.country,
ny4.value.organization,
]);
}
else {
updateDom("check-ny4-ip", "Fetch Failed");
updateDom("check-ny4-location", "Fetch Failed");
console.log("Fetch failed:", ny4.reason);
}
if (ipsb.status === "fulfilled") {
updateDom("check-ipsb-ip", ipsb.value.ip);
updateDom("check-ipsb-location", [
ipsb.value.city,
ipsb.value.region,
ipsb.value.country,
ipsb.value.isp,
]);
}
else {
updateDom("check-ipsb-ip", "Fetch Failed");
updateDom("check-ipsb-location", "Fetch Failed");
console.log("Fetch failed:", ipsb.reason);
}
if (speedtestcn.status === "fulfilled") {
updateDom("check-speedtestcn-ip", speedtestcn.value.data.ip);
updateDom("check-speedtestcn-location", [
speedtestcn.value.data.city,
speedtestcn.value.data.province,
speedtestcn.value.data.country,
speedtestcn.value.data.isp,
]);
}
else {
updateDom("check-speedtestcn-ip", "Fetch Failed");
updateDom("check-speedtestcn-location", "Fetch Failed");
console.log("Fetch failed:", speedtestcn.reason);
}
if (ipapiis.status === "fulfilled") {
updateDom("check-ipapiis-ip", ipapiis.value.ip);
updateDom("check-ipapiis-location", [
ipapiis.value.location.city,
ipapiis.value.location.state,
ipapiis.value.location.country,
ipapiis.value.company.name,
]);
}
else {
updateDom("check-ipapiis-ip", "Fetch Failed");
updateDom("check-ipapiis-location", "Fetch Failed");
console.log("Fetch failed:", ipapiis.reason);
}
});
}
function fetchAsync(url) {
return __awaiter(this, void 0, void 0, function* () {
try {
const response = yield fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return yield response.json();
}
catch (error) {
console.log("Fetch error:", error);
throw error;
}
});
}
function updateDom(elementId, content) {
document.getElementById(elementId).innerHTML = Array.isArray(content)
? content.join(", ")
: content;
}
window.onload = main;

View file

@ -1,5 +1,3 @@
@import url("https://fonts.googleapis.com/css2?family=Fira+Code:wght@500&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;

1
ui/src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="vite/client" />

View file

@ -1,11 +1,11 @@
/** @type {import('tailwindcss').Config} */
const { fontFamily } = require('tailwindcss/defaultTheme')
const { fontFamily } = require("tailwindcss/defaultTheme");
module.exports = {
content: ["./**/*.{html,js}"],
content: ["./src/**/*.{html,js}", "./index.html"],
theme: {
fontFamily: {
mono: ['"Fira Code"', ...fontFamily.mono,],
mono: ['"Fira Code"', ...fontFamily.mono],
},
extend: {},
},

24
ui/tsconfig.json Normal file
View file

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}