Refactor type usage and improve data normalization

Updated several components to use explicit TypeScript types for better type safety. Normalized appriseUrls to always be an array in auto-sync settings API. Improved handling of optional server_id in BackupsTab and adjusted IP detection logic in InstalledScriptsTab. Removed unnecessary eslint-disable comments and improved code clarity in various places.
This commit is contained in:
CanbiZ
2025-11-28 12:47:09 +01:00
parent 7547dff67d
commit 03e31d66a7
11 changed files with 160 additions and 138 deletions

View File

@@ -1,15 +1,23 @@
import { FlatCompat } from "@eslint/eslintrc";
import eslintPluginNext from "@next/eslint-plugin-next";
import tseslint from "typescript-eslint";
const compat = new FlatCompat({
baseDirectory: import.meta.dirname,
});
import reactPlugin from "eslint-plugin-react";
import reactHooksPlugin from "eslint-plugin-react-hooks";
export default tseslint.config(
{
ignores: [".next", "next-env.d.ts", "postcss.config.js", "prettier.config.js"],
},
...compat.extends("next/core-web-vitals"),
{
plugins: {
"@next/next": eslintPluginNext,
"react": reactPlugin,
"react-hooks": reactHooksPlugin,
},
rules: {
...eslintPluginNext.configs.recommended.rules,
...eslintPluginNext.configs["core-web-vitals"].rules,
},
},
{
files: ["**/*.ts", "**/*.tsx"],
extends: [

View File

@@ -97,7 +97,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
const checkAuth = useCallback(() => {
return checkAuthInternal(0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const login = async (

View File

@@ -32,7 +32,7 @@ interface Backup {
storage_name: string;
storage_type: string;
discovered_at: Date;
server_id: number;
server_id?: number;
server_name: string | null;
server_color: string | null;
}
@@ -163,7 +163,6 @@ export function BackupsTab() {
}
setHasAutoDiscovered(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasAutoDiscovered, isLoading, backupsData]);
const handleDiscoverBackups = () => {
@@ -188,7 +187,7 @@ export function BackupsTab() {
restoreMutation.mutate({
backupId: selectedBackup.backup.id,
containerId: selectedBackup.containerId,
serverId: selectedBackup.backup.server_id,
serverId: selectedBackup.backup.server_id ?? 0,
});
};

View File

@@ -321,7 +321,7 @@ export function FilterBar({
{/* Repository Filter Buttons - Only show if more than one enabled repo */}
{enabledRepos.length > 1 &&
enabledRepos.map((repo) => {
enabledRepos.map((repo: { id: number; url: string }) => {
const repoUrl = String(repo.url);
const isSelected =
filters.selectedRepositories.includes(repoUrl);

View File

@@ -1704,7 +1704,15 @@ export function GeneralSettingsModal({
{repositoriesData?.success &&
repositoriesData.repositories.length > 0 ? (
<div className="space-y-3">
{repositoriesData.repositories.map((repo) => (
{repositoriesData.repositories.map(
(repo: {
id: number;
url: string;
enabled: boolean;
is_default: boolean;
is_removable: boolean;
priority: number;
}) => (
<div
key={repo.id}
className="border-border flex items-center justify-between gap-3 rounded-lg border p-3"
@@ -1831,7 +1839,8 @@ export function GeneralSettingsModal({
</Button>
</div>
</div>
))}
),
)}
</div>
) : (
<p className="text-muted-foreground text-sm">

View File

@@ -331,7 +331,7 @@ export function InstalledScriptsTab() {
setAutoDetectStatus({
type: "success",
message: data.success
? `Detected IP: ${data.ip}`
? `Detected IP: ${data.detectedIp ?? "unknown"}`
: (data.error ?? "Failed to detect Web UI"),
});
setTimeout(
@@ -359,15 +359,10 @@ export function InstalledScriptsTab() {
{ enabled: false }, // Only fetch when explicitly called
);
const fetchStorages = async (serverId: number, forceRefresh = false) => {
const fetchStorages = async (serverId: number, _forceRefresh = false) => {
setIsLoadingStorages(true);
try {
const result = await getBackupStoragesQuery.refetch({
queryKey: [
"installedScripts.getBackupStorages",
{ serverId, forceRefresh },
],
});
const result = await getBackupStoragesQuery.refetch();
if (result.data?.success) {
setBackupStorages(result.data.storages);
} else {

View File

@@ -74,7 +74,7 @@ export function ServerForm({
// Check for IPv6 with zone identifier (link-local addresses like fe80::...%eth0)
let ipv6Address = trimmed;
const zoneIdMatch = /^(.+)%([a-zA-Z0-9_\-]+)$/.exec(trimmed);
if (zoneIdMatch) {
if (zoneIdMatch?.[1] && zoneIdMatch[2]) {
ipv6Address = zoneIdMatch[1];
// Zone identifier should be a valid interface name (alphanumeric, underscore, hyphen)
const zoneId = zoneIdMatch[2];

View File

@@ -380,7 +380,7 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
wsRef.current.close();
}
};
}, [scriptPath, mode, server, isUpdate, isShell, containerId, isMobile]); // eslint-disable-line react-hooks/exhaustive-deps
}, [scriptPath, mode, server, isUpdate, isShell, containerId, isMobile]);
const startScript = () => {
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN && !isRunning) {

View File

@@ -187,7 +187,16 @@ export async function POST(request: NextRequest) {
}
// Update the global service instance with new settings
autoSyncService.saveSettings(settings);
// Normalize appriseUrls to always be an array
const normalizedSettings = {
...settings,
appriseUrls: Array.isArray(settings.appriseUrls)
? settings.appriseUrls
: settings.appriseUrls
? [settings.appriseUrls]
: undefined
};
autoSyncService.saveSettings(normalizedSettings);
if (settings.autoSyncEnabled) {
autoSyncService.scheduleAutoSync();

View File

@@ -476,7 +476,8 @@ export const installedScriptsRouter = createTRPCRouter({
const scripts = await db.getAllInstalledScripts();
// Transform scripts to flatten server data for frontend compatibility
const transformedScripts = await Promise.all(scripts.map(async (script) => {
const transformedScripts = await Promise.all(scripts.map(async (script: any) => {
// Determine if it's a VM or LXC
let is_vm = false;
if (script.container_id && script.server_id) {
@@ -522,7 +523,8 @@ export const installedScriptsRouter = createTRPCRouter({
const scripts = await db.getInstalledScriptsByServer(input.serverId);
// Transform scripts to flatten server data for frontend compatibility
const transformedScripts = await Promise.all(scripts.map(async (script) => {
const transformedScripts = await Promise.all(scripts.map(async (script: any) => {
// Determine if it's a VM or LXC
let is_vm = false;
if (script.container_id && script.server_id) {

View File

@@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { z } from "zod";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
import { scriptManager } from "~/server/lib/scripts";