mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-17 16:01:22 +02:00
Merge branch 'dev' into Lokowitz-feature-i18n
This commit is contained in:
commit
363b8b52af
11 changed files with 355 additions and 425 deletions
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
|
@ -13,10 +13,6 @@ updates:
|
||||||
dependency-type: "development"
|
dependency-type: "development"
|
||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- "minor"
|
||||||
dev-major-updates:
|
|
||||||
dependency-type: "development"
|
|
||||||
update-types:
|
|
||||||
- "major"
|
|
||||||
prod-patch-updates:
|
prod-patch-updates:
|
||||||
dependency-type: "production"
|
dependency-type: "production"
|
||||||
update-types:
|
update-types:
|
||||||
|
@ -25,10 +21,6 @@ updates:
|
||||||
dependency-type: "production"
|
dependency-type: "production"
|
||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- "minor"
|
||||||
prod-major-updates:
|
|
||||||
dependency-type: "production"
|
|
||||||
update-types:
|
|
||||||
- "major"
|
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
|
@ -41,6 +33,3 @@ updates:
|
||||||
minor-updates:
|
minor-updates:
|
||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- "minor"
|
||||||
major-updates:
|
|
||||||
update-types:
|
|
||||||
- "major"
|
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -32,5 +32,4 @@ installer
|
||||||
bin
|
bin
|
||||||
.secrets
|
.secrets
|
||||||
test_event.json
|
test_event.json
|
||||||
.idea/
|
.idea/
|
||||||
package-lock.json
|
|
655
package-lock.json
generated
655
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -106,7 +106,7 @@
|
||||||
"winston": "3.17.0",
|
"winston": "3.17.0",
|
||||||
"winston-daily-rotate-file": "5.0.0",
|
"winston-daily-rotate-file": "5.0.0",
|
||||||
"ws": "8.18.2",
|
"ws": "8.18.2",
|
||||||
"zod": "3.25.46",
|
"zod": "3.25.56",
|
||||||
"zod-validation-error": "3.4.1"
|
"zod-validation-error": "3.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -114,8 +114,8 @@
|
||||||
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
||||||
"@tailwindcss/postcss": "^4.1.8",
|
"@tailwindcss/postcss": "^4.1.8",
|
||||||
"@types/better-sqlite3": "7.6.12",
|
"@types/better-sqlite3": "7.6.12",
|
||||||
"@types/cookie-parser": "1.4.8",
|
"@types/cookie-parser": "1.4.9",
|
||||||
"@types/cors": "2.8.18",
|
"@types/cors": "2.8.19",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/express": "5.0.0",
|
"@types/express": "5.0.0",
|
||||||
"@types/jmespath": "^0.15.2",
|
"@types/jmespath": "^0.15.2",
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
"@types/node": "^22",
|
"@types/node": "^22",
|
||||||
"@types/nodemailer": "6.4.17",
|
"@types/nodemailer": "6.4.17",
|
||||||
"@types/react": "19.1.6",
|
"@types/react": "19.1.6",
|
||||||
"@types/react-dom": "19.1.5",
|
"@types/react-dom": "19.1.6",
|
||||||
"@types/semver": "7.7.0",
|
"@types/semver": "7.7.0",
|
||||||
"@types/swagger-ui-express": "^4.1.8",
|
"@types/swagger-ui-express": "^4.1.8",
|
||||||
"@types/ws": "8.18.1",
|
"@types/ws": "8.18.1",
|
||||||
|
@ -133,7 +133,7 @@
|
||||||
"esbuild": "0.25.5",
|
"esbuild": "0.25.5",
|
||||||
"esbuild-node-externals": "1.18.0",
|
"esbuild-node-externals": "1.18.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"react-email": "4.0.15",
|
"react-email": "4.0.16",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.1.4",
|
||||||
"tsc-alias": "1.8.16",
|
"tsc-alias": "1.8.16",
|
||||||
"tsx": "4.19.4",
|
"tsx": "4.19.4",
|
||||||
|
|
|
@ -2,7 +2,7 @@ import path from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
// This is a placeholder value replaced by the build process
|
// This is a placeholder value replaced by the build process
|
||||||
export const APP_VERSION = "1.5.0";
|
export const APP_VERSION = "1.5.1";
|
||||||
|
|
||||||
export const __FILENAME = fileURLToPath(import.meta.url);
|
export const __FILENAME = fileURLToPath(import.meta.url);
|
||||||
export const __DIRNAME = path.dirname(__FILENAME);
|
export const __DIRNAME = path.dirname(__FILENAME);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { createApiClient } from "@app/lib/api";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import CreateSiteFormModal from "./CreateSiteModal";
|
import CreateSiteFormModal from "./CreateSiteModal";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import { parseDataSize } from '@app/lib/dataSize';
|
||||||
|
|
||||||
export type SiteRow = {
|
export type SiteRow = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -199,7 +200,9 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
sortingFn: (rowA, rowB) =>
|
||||||
|
parseDataSize(rowA.original.mbIn) - parseDataSize(rowB.original.mbIn)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "mbOut",
|
accessorKey: "mbOut",
|
||||||
|
@ -215,7 +218,9 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
sortingFn: (rowA, rowB) =>
|
||||||
|
parseDataSize(rowA.original.mbOut) - parseDataSize(rowB.original.mbOut),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "type",
|
accessorKey: "type",
|
||||||
|
|
|
@ -33,6 +33,15 @@ import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { SwitchInput } from "@app/components/SwitchInput";
|
import { SwitchInput } from "@app/components/SwitchInput";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { ArrowRight } from "lucide-react";
|
||||||
|
|
||||||
|
const GeneralFormSchema = z.object({
|
||||||
|
name: z.string().nonempty("Name is required"),
|
||||||
|
dockerSocketEnabled: z.boolean().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
|
||||||
|
|
||||||
export default function GeneralPage() {
|
export default function GeneralPage() {
|
||||||
const { site, updateSite } = useSiteContext();
|
const { site, updateSite } = useSiteContext();
|
||||||
|
@ -154,6 +163,20 @@ export default function GeneralPage() {
|
||||||
discovery for populating
|
discovery for populating
|
||||||
container information,
|
container information,
|
||||||
useful in resource targets.
|
useful in resource targets.
|
||||||
|
<Link
|
||||||
|
href="https://docs.fossorial.io/Newt/overview#docker-socket-integration"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{" "}
|
||||||
|
Docker socket path
|
||||||
|
must be provided to
|
||||||
|
Newt in order to use
|
||||||
|
this feature.
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -84,7 +84,14 @@ export default function StepperForm() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const generateId = (name: string) => {
|
const generateId = (name: string) => {
|
||||||
return name.toLowerCase().replace(/\s+/g, "-");
|
// Replace any character that is not a letter, number, space, or hyphen with a hyphen
|
||||||
|
// Also collapse multiple hyphens and trim
|
||||||
|
return name
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9\s-]/g, "-")
|
||||||
|
.replace(/\s+/g, "-")
|
||||||
|
.replace(/-+/g, "-")
|
||||||
|
.replace(/^-+|-+$/g, "");
|
||||||
};
|
};
|
||||||
|
|
||||||
async function orgSubmit(values: z.infer<typeof orgSchema>) {
|
async function orgSubmit(values: z.infer<typeof orgSchema>) {
|
||||||
|
@ -209,23 +216,22 @@ export default function StepperForm() {
|
||||||
type="text"
|
type="text"
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const orgId =
|
// Prevent "/" in orgName input
|
||||||
generateId(
|
const sanitizedValue = e.target.value.replace(/\//g, "-");
|
||||||
e.target
|
const orgId = generateId(sanitizedValue);
|
||||||
.value
|
|
||||||
);
|
|
||||||
orgForm.setValue(
|
orgForm.setValue(
|
||||||
"orgId",
|
"orgId",
|
||||||
orgId
|
orgId
|
||||||
);
|
);
|
||||||
orgForm.setValue(
|
orgForm.setValue(
|
||||||
"orgName",
|
"orgName",
|
||||||
e.target.value
|
sanitizedValue
|
||||||
);
|
);
|
||||||
debouncedCheckOrgIdAvailability(
|
debouncedCheckOrgIdAvailability(
|
||||||
orgId
|
orgId
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
value={field.value.replace(/\//g, "-")}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|
|
@ -346,16 +346,19 @@ const DockerContainersTable: FC<{
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
header: "Actions",
|
header: "Actions",
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => {
|
||||||
<Button
|
const ports = getExposedPorts(row.original);
|
||||||
variant="default"
|
return (
|
||||||
size="sm"
|
<Button
|
||||||
onClick={() => onContainerSelect(row.original)}
|
variant="default"
|
||||||
disabled={row.original.state !== "running"}
|
size="sm"
|
||||||
>
|
onClick={() => onContainerSelect(row.original, ports[0])}
|
||||||
Select
|
disabled={row.original.state !== "running"}
|
||||||
</Button>
|
>
|
||||||
)
|
Select
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ export function useDockerSocket(site: Site) {
|
||||||
|
|
||||||
const api = createApiClient(useEnvContext());
|
const api = createApiClient(useEnvContext());
|
||||||
|
|
||||||
const { dockerSocketEnabled: isEnabled = true } = site || {};
|
const { dockerSocketEnabled: rawIsEnabled = true, type: siteType } = site || {};
|
||||||
|
const isEnabled = rawIsEnabled && siteType === "newt";
|
||||||
const { isAvailable = false, socketPath } = dockerSocket || {};
|
const { isAvailable = false, socketPath } = dockerSocket || {};
|
||||||
|
|
||||||
const checkDockerSocket = useCallback(async () => {
|
const checkDockerSocket = useCallback(async () => {
|
||||||
|
|
21
src/lib/dataSize.ts
Normal file
21
src/lib/dataSize.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export function parseDataSize(sizeStr: string): number {
|
||||||
|
if (typeof sizeStr !== 'string') return 0;
|
||||||
|
|
||||||
|
const match = /^\s*([\d.]+)\s*([KMGT]?B)\s*$/i.exec(sizeStr);
|
||||||
|
if (!match) return 0;
|
||||||
|
|
||||||
|
const [ , numStr, unitRaw ] = match;
|
||||||
|
const num = parseFloat(numStr);
|
||||||
|
if (isNaN(num)) return 0;
|
||||||
|
|
||||||
|
const unit = unitRaw.toUpperCase();
|
||||||
|
const multipliers = {
|
||||||
|
B: 1,
|
||||||
|
KB: 1024,
|
||||||
|
MB: 1024 ** 2,
|
||||||
|
GB: 1024 ** 3,
|
||||||
|
TB: 1024 ** 4,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
return num * (multipliers[unit as keyof typeof multipliers] ?? 1);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue