mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Change to use dpdm for circular import scanning (#5788)
Co-authored-by: Anthony Stirling <[email protected]>
This commit is contained in:
co-authored by
Anthony Stirling
parent
1b3bfaec20
commit
eab84a13d0
@@ -4,7 +4,6 @@ import eslint from '@eslint/js';
|
|||||||
import globals from 'globals';
|
import globals from 'globals';
|
||||||
import { defineConfig } from 'eslint/config';
|
import { defineConfig } from 'eslint/config';
|
||||||
import tseslint from 'typescript-eslint';
|
import tseslint from 'typescript-eslint';
|
||||||
import importPlugin from 'eslint-plugin-import';
|
|
||||||
|
|
||||||
const srcGlobs = [
|
const srcGlobs = [
|
||||||
'src/**/*.{js,mjs,jsx,ts,tsx}',
|
'src/**/*.{js,mjs,jsx,ts,tsx}',
|
||||||
@@ -78,21 +77,4 @@ export default defineConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Config for import plugin
|
|
||||||
{
|
|
||||||
...importPlugin.flatConfigs.recommended,
|
|
||||||
...importPlugin.flatConfigs.typescript,
|
|
||||||
rules: {
|
|
||||||
// ...importPlugin.flatConfigs.recommended.rules, // Temporarily disabled until codebase conformant
|
|
||||||
...importPlugin.flatConfigs.typescript.rules,
|
|
||||||
'import/no-cycle': 'error',
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
'import/resolver': {
|
|
||||||
typescript: {
|
|
||||||
project: './tsconfig.json',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|||||||
Generated
+58
-2108
File diff suppressed because it is too large
Load Diff
@@ -77,7 +77,9 @@
|
|||||||
"predev": "npm run generate-icons",
|
"predev": "npm run generate-icons",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"prebuild": "npm run generate-icons",
|
"prebuild": "npm run generate-icons",
|
||||||
"lint": "eslint --max-warnings=0",
|
"lint": "npm run lint:eslint && npm run lint:cycles",
|
||||||
|
"lint:eslint": "eslint --max-warnings=0",
|
||||||
|
"lint:cycles": "dpdm src --circular --no-warning --no-tree --exit-code circular:1",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri-dev": "tauri dev --no-watch",
|
"tauri-dev": "tauri dev --no-watch",
|
||||||
@@ -149,9 +151,8 @@
|
|||||||
"@typescript-eslint/parser": "^8.44.1",
|
"@typescript-eslint/parser": "^8.44.1",
|
||||||
"@vitejs/plugin-react-swc": "^4.1.0",
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
|
"dpdm": "^3.14.0",
|
||||||
"eslint": "^9.36.0",
|
"eslint": "^9.36.0",
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
|
||||||
"eslint-plugin-import": "^2.32.0",
|
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"jsdom": "^27.0.0",
|
"jsdom": "^27.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
|
||||||
import apiClient from '@app/services/apiClient';
|
import apiClient from '@app/services/apiClient';
|
||||||
import { getSimulatedAppConfig } from '@app/testing/serverExperienceSimulations';
|
import { getSimulatedAppConfig } from '@app/testing/serverExperienceSimulations';
|
||||||
|
import type { AppConfig, AppConfigBootstrapMode } from '@app/types/appConfig';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sleep utility for delays
|
* Sleep utility for delays
|
||||||
@@ -14,55 +15,7 @@ export interface AppConfigRetryOptions {
|
|||||||
initialDelay?: number;
|
initialDelay?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppConfig {
|
export type { AppConfig, AppConfigBootstrapMode };
|
||||||
baseUrl?: string;
|
|
||||||
contextPath?: string;
|
|
||||||
serverPort?: number;
|
|
||||||
frontendUrl?: string;
|
|
||||||
appNameNavbar?: string;
|
|
||||||
languages?: string[];
|
|
||||||
defaultLocale?: string;
|
|
||||||
logoStyle?: 'modern' | 'classic';
|
|
||||||
enableLogin?: boolean;
|
|
||||||
showSettingsWhenNoLogin?: boolean;
|
|
||||||
enableEmailInvites?: boolean;
|
|
||||||
enableOAuth?: boolean;
|
|
||||||
enableSaml?: boolean;
|
|
||||||
isAdmin?: boolean;
|
|
||||||
enableAlphaFunctionality?: boolean;
|
|
||||||
enableAnalytics?: boolean | null;
|
|
||||||
enablePosthog?: boolean | null;
|
|
||||||
enableScarf?: boolean | null;
|
|
||||||
enableDesktopInstallSlide?: boolean;
|
|
||||||
premiumEnabled?: boolean;
|
|
||||||
premiumKey?: string;
|
|
||||||
termsAndConditions?: string;
|
|
||||||
privacyPolicy?: string;
|
|
||||||
cookiePolicy?: string;
|
|
||||||
impressum?: string;
|
|
||||||
accessibilityStatement?: string;
|
|
||||||
runningProOrHigher?: boolean;
|
|
||||||
runningEE?: boolean;
|
|
||||||
license?: string;
|
|
||||||
SSOAutoLogin?: boolean;
|
|
||||||
serverCertificateEnabled?: boolean;
|
|
||||||
enableMobileScanner?: boolean;
|
|
||||||
mobileScannerConvertToPdf?: boolean;
|
|
||||||
mobileScannerImageResolution?: string;
|
|
||||||
mobileScannerPageFormat?: string;
|
|
||||||
mobileScannerStretchToFit?: boolean;
|
|
||||||
appVersion?: string;
|
|
||||||
machineType?: string;
|
|
||||||
activeSecurity?: boolean;
|
|
||||||
dependenciesReady?: boolean;
|
|
||||||
error?: string;
|
|
||||||
isNewServer?: boolean;
|
|
||||||
isNewUser?: boolean;
|
|
||||||
defaultHideUnavailableTools?: boolean;
|
|
||||||
defaultHideUnavailableConversions?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AppConfigBootstrapMode = 'blocking' | 'non-blocking';
|
|
||||||
|
|
||||||
interface AppConfigContextValue {
|
interface AppConfigContextValue {
|
||||||
config: AppConfig | null;
|
config: AppConfig | null;
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ import { downloadFiles } from '@app/utils/downloadUtils';
|
|||||||
import { FileId } from '@app/types/file';
|
import { FileId } from '@app/types/file';
|
||||||
import { groupFilesByOriginal } from '@app/utils/fileHistoryUtils';
|
import { groupFilesByOriginal } from '@app/utils/fileHistoryUtils';
|
||||||
import { openFilesFromDisk } from '@app/services/openFilesFromDisk';
|
import { openFilesFromDisk } from '@app/services/openFilesFromDisk';
|
||||||
|
export { pendingFilePathMappings } from '@app/services/pendingFilePathMappings';
|
||||||
// Module-level storage for file path mappings (quickKey -> localFilePath)
|
|
||||||
// Used to pass file paths from Tauri file dialog to FileContext
|
|
||||||
export const pendingFilePathMappings = new Map<string, string>();
|
|
||||||
|
|
||||||
// Type for the context value - now contains everything directly
|
// Type for the context value - now contains everything directly
|
||||||
interface FileManagerContextValue {
|
interface FileManagerContextValue {
|
||||||
|
|||||||
@@ -1,38 +1,17 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { signatureStorageService, type StorageType } from '@app/services/signatureStorageService';
|
import { signatureStorageService, type StorageType } from '@app/services/signatureStorageService';
|
||||||
import { useAppConfig } from '@app/contexts/AppConfigContext';
|
import { useAppConfig } from '@app/contexts/AppConfigContext';
|
||||||
|
import type {
|
||||||
|
SavedSignature,
|
||||||
|
SavedSignaturePayload,
|
||||||
|
SavedSignatureType,
|
||||||
|
SignatureScope,
|
||||||
|
} from '@app/types/signature';
|
||||||
|
|
||||||
export const MAX_SAVED_SIGNATURES_BACKEND = 20; // Backend limit per user
|
export const MAX_SAVED_SIGNATURES_BACKEND = 20; // Backend limit per user
|
||||||
export const MAX_SAVED_SIGNATURES_LOCALSTORAGE = 10; // LocalStorage limit
|
export const MAX_SAVED_SIGNATURES_LOCALSTORAGE = 10; // LocalStorage limit
|
||||||
|
|
||||||
export type SavedSignatureType = 'canvas' | 'image' | 'text';
|
export type { SavedSignature, SavedSignaturePayload, SavedSignatureType, SignatureScope };
|
||||||
export type SignatureScope = 'personal' | 'shared' | 'localStorage';
|
|
||||||
|
|
||||||
export type SavedSignaturePayload =
|
|
||||||
| {
|
|
||||||
type: 'canvas';
|
|
||||||
dataUrl: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'image';
|
|
||||||
dataUrl: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'text';
|
|
||||||
dataUrl: string;
|
|
||||||
signerName: string;
|
|
||||||
fontFamily: string;
|
|
||||||
fontSize: number;
|
|
||||||
textColor: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SavedSignature = SavedSignaturePayload & {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
scope: SignatureScope;
|
|
||||||
createdAt: number;
|
|
||||||
updatedAt: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AddSignatureResult =
|
export type AddSignatureResult =
|
||||||
| { success: true; signature: SavedSignature }
|
| { success: true; signature: SavedSignature }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { openFileDialog } from '@app/services/fileDialogService';
|
import { openFileDialog } from '@app/services/fileDialogService';
|
||||||
import { pendingFilePathMappings } from '@app/contexts/FileManagerContext';
|
import { pendingFilePathMappings } from '@app/services/pendingFilePathMappings';
|
||||||
import { getDocumentFileDialogFilter } from '@app/utils/fileDialogUtils';
|
import { getDocumentFileDialogFilter } from '@app/utils/fileDialogUtils';
|
||||||
|
|
||||||
interface OpenFilesFromDiskOptions {
|
interface OpenFilesFromDiskOptions {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Module-level storage for file path mappings (quickKey -> localFilePath)
|
||||||
|
// Used to pass file paths from Tauri file dialog to FileManagerContext
|
||||||
|
export const pendingFilePathMappings = new Map<string, string>();
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import apiClient from '@app/services/apiClient';
|
import apiClient from '@app/services/apiClient';
|
||||||
import type { SavedSignature } from '@app/hooks/tools/sign/useSavedSignatures';
|
import type { SavedSignature } from '@app/types/signature';
|
||||||
|
|
||||||
export type StorageType = 'backend' | 'localStorage';
|
export type StorageType = 'backend' | 'localStorage';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
export interface AppConfig {
|
||||||
|
baseUrl?: string;
|
||||||
|
contextPath?: string;
|
||||||
|
serverPort?: number;
|
||||||
|
frontendUrl?: string;
|
||||||
|
appNameNavbar?: string;
|
||||||
|
languages?: string[];
|
||||||
|
defaultLocale?: string;
|
||||||
|
logoStyle?: 'modern' | 'classic';
|
||||||
|
enableLogin?: boolean;
|
||||||
|
showSettingsWhenNoLogin?: boolean;
|
||||||
|
enableEmailInvites?: boolean;
|
||||||
|
enableOAuth?: boolean;
|
||||||
|
enableSaml?: boolean;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
enableAlphaFunctionality?: boolean;
|
||||||
|
enableAnalytics?: boolean | null;
|
||||||
|
enablePosthog?: boolean | null;
|
||||||
|
enableScarf?: boolean | null;
|
||||||
|
enableDesktopInstallSlide?: boolean;
|
||||||
|
premiumEnabled?: boolean;
|
||||||
|
premiumKey?: string;
|
||||||
|
termsAndConditions?: string;
|
||||||
|
privacyPolicy?: string;
|
||||||
|
cookiePolicy?: string;
|
||||||
|
impressum?: string;
|
||||||
|
accessibilityStatement?: string;
|
||||||
|
runningProOrHigher?: boolean;
|
||||||
|
runningEE?: boolean;
|
||||||
|
license?: string;
|
||||||
|
SSOAutoLogin?: boolean;
|
||||||
|
serverCertificateEnabled?: boolean;
|
||||||
|
enableMobileScanner?: boolean;
|
||||||
|
mobileScannerConvertToPdf?: boolean;
|
||||||
|
mobileScannerImageResolution?: string;
|
||||||
|
mobileScannerPageFormat?: string;
|
||||||
|
mobileScannerStretchToFit?: boolean;
|
||||||
|
appVersion?: string;
|
||||||
|
machineType?: string;
|
||||||
|
activeSecurity?: boolean;
|
||||||
|
dependenciesReady?: boolean;
|
||||||
|
error?: string;
|
||||||
|
isNewServer?: boolean;
|
||||||
|
isNewUser?: boolean;
|
||||||
|
defaultHideUnavailableTools?: boolean;
|
||||||
|
defaultHideUnavailableConversions?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppConfigBootstrapMode = 'blocking' | 'non-blocking';
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
export type SavedSignatureType = 'canvas' | 'image' | 'text';
|
||||||
|
export type SignatureScope = 'personal' | 'shared' | 'localStorage';
|
||||||
|
|
||||||
|
export type SavedSignaturePayload =
|
||||||
|
| {
|
||||||
|
type: 'canvas';
|
||||||
|
dataUrl: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'image';
|
||||||
|
dataUrl: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'text';
|
||||||
|
dataUrl: string;
|
||||||
|
signerName: string;
|
||||||
|
fontFamily: string;
|
||||||
|
fontSize: number;
|
||||||
|
textColor: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SavedSignature = SavedSignaturePayload & {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
scope: SignatureScope;
|
||||||
|
createdAt: number;
|
||||||
|
updatedAt: number;
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
|
const TOKEN_KEY = 'stirling_jwt';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read auth token from any available source (Tauri store or localStorage).
|
||||||
|
* Kept separate to avoid circular dependencies between auth and backend services.
|
||||||
|
*/
|
||||||
|
export async function getAuthTokenFromAnySource(): Promise<string | null> {
|
||||||
|
// Try Tauri store first
|
||||||
|
try {
|
||||||
|
const token = await invoke<string | null>('get_auth_token');
|
||||||
|
if (token) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Desktop AuthTokenStore] Failed to read from Tauri store:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to localStorage
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(TOKEN_KEY);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Desktop AuthTokenStore] Failed to read from localStorage:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { fetch } from '@tauri-apps/plugin-http';
|
import { fetch } from '@tauri-apps/plugin-http';
|
||||||
import { connectionModeService } from '@app/services/connectionModeService';
|
import { connectionModeService } from '@app/services/connectionModeService';
|
||||||
|
import { getAuthTokenFromAnySource } from '@app/services/authTokenStore';
|
||||||
|
|
||||||
export type BackendStatus = 'stopped' | 'starting' | 'healthy' | 'unhealthy';
|
export type BackendStatus = 'stopped' | 'starting' | 'healthy' | 'unhealthy';
|
||||||
|
|
||||||
@@ -122,8 +123,7 @@ export class TauriBackendService {
|
|||||||
*/
|
*/
|
||||||
private async getAuthToken(): Promise<string | null> {
|
private async getAuthToken(): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
const { authService } = await import('./authService');
|
return await getAuthTokenFromAnySource();
|
||||||
return await authService.getAuthToken();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.debug('[TauriBackendService] Failed to get auth token:', error);
|
console.debug('[TauriBackendService] Failed to get auth token:', error);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PlanFeature } from '@app/services/licenseService';
|
import type { PlanFeature } from '@app/types/license';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared plan feature definitions for Stirling PDF Self-Hosted
|
* Shared plan feature definitions for Stirling PDF Self-Hosted
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import apiClient from '@app/services/apiClient';
|
|||||||
import { supabase, isSupabaseConfigured } from '@app/services/supabaseClient';
|
import { supabase, isSupabaseConfigured } from '@app/services/supabaseClient';
|
||||||
import { getCheckoutMode } from '@app/utils/protocolDetection';
|
import { getCheckoutMode } from '@app/utils/protocolDetection';
|
||||||
import { PLAN_FEATURES, PLAN_HIGHLIGHTS } from '@app/constants/planConstants';
|
import { PLAN_FEATURES, PLAN_HIGHLIGHTS } from '@app/constants/planConstants';
|
||||||
|
import type { LicenseInfo, PlanFeature } from '@app/types/license';
|
||||||
export interface PlanFeature {
|
|
||||||
name: string;
|
|
||||||
included: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PlanTier {
|
export interface PlanTier {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -69,13 +65,7 @@ export interface LicenseKeyResponse {
|
|||||||
plan?: string;
|
plan?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LicenseInfo {
|
export type { LicenseInfo, PlanFeature };
|
||||||
licenseType: 'NORMAL' | 'SERVER' | 'ENTERPRISE';
|
|
||||||
enabled: boolean;
|
|
||||||
maxUsers: number;
|
|
||||||
hasKey: boolean;
|
|
||||||
licenseKey?: string; // The actual license key (for upgrades)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LicenseSaveResponse {
|
export interface LicenseSaveResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { AppConfig } from '@app/contexts/AppConfigContext';
|
import type { AppConfig } from '@app/types/appConfig';
|
||||||
import type { LicenseInfo } from '@app/services/licenseService';
|
import type { LicenseInfo } from '@app/types/license';
|
||||||
|
|
||||||
interface WauResponse {
|
interface WauResponse {
|
||||||
trackingSince: string;
|
trackingSince: string;
|
||||||
@@ -208,4 +208,3 @@ export function getSimulatedLicenseInfo(): LicenseInfo | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DEV_TESTING_ENABLED = DEV_TESTING_MODE;
|
export const DEV_TESTING_ENABLED = DEV_TESTING_MODE;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export interface PlanFeature {
|
||||||
|
name: string;
|
||||||
|
included: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LicenseInfo {
|
||||||
|
licenseType: 'NORMAL' | 'SERVER' | 'ENTERPRISE';
|
||||||
|
enabled: boolean;
|
||||||
|
maxUsers: number;
|
||||||
|
hasKey: boolean;
|
||||||
|
licenseKey?: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user