Refactor types

This commit is contained in:
2025-05-22 21:24:39 +02:00
parent 2f181d0f7f
commit 32218e5595
20 changed files with 457 additions and 418 deletions

View File

@@ -1,60 +0,0 @@
import type { UserRole } from './authApi';
// CreateUserRequest holds the request fields for creating a new user
export interface CreateUserRequest {
email: string;
displayName: string;
password: string;
role: UserRole;
}
// UpdateUserRequest holds the request fields for updating a user
export interface UpdateUserRequest {
email?: string;
displayName?: string;
password?: string;
role?: UserRole;
}
// WorkspaceStats holds workspace statistics
export interface WorkspaceStats {
userID: number;
userEmail: string;
workspaceID: number;
workspaceName: string;
workspaceCreatedAt: string; // Using ISO string format for time.Time
fileCountStats?: FileCountStats;
}
// Define FileCountStats based on the Go struct definition of storage.FileCountStats
export interface FileCountStats {
totalFiles: number;
totalSize: number;
}
export interface UserStats {
totalUsers: number;
totalWorkspaces: number;
activeUsers: number; // Users with activity in last 30 days
}
// SystemStats holds system-wide statistics
export interface SystemStats extends FileCountStats, UserStats {}
// isSystemStats checks if the given object is a valid SystemStats object
export function isSystemStats(obj: unknown): obj is SystemStats {
return (
typeof obj === 'object' &&
obj !== null &&
'totalUsers' in obj &&
typeof (obj as SystemStats).totalUsers === 'number' &&
'totalWorkspaces' in obj &&
typeof (obj as SystemStats).totalWorkspaces === 'number' &&
'activeUsers' in obj &&
typeof (obj as SystemStats).activeUsers === 'number' &&
'totalFiles' in obj &&
typeof (obj as SystemStats).totalFiles === 'number' &&
'totalSize' in obj &&
typeof (obj as SystemStats).totalSize === 'number'
);
}

116
app/src/types/api.ts Normal file
View File

@@ -0,0 +1,116 @@
import { isUser, type User, type UserRole } from './models';
declare global {
interface Window {
API_BASE_URL: string;
}
}
export const API_BASE_URL = window.API_BASE_URL;
/**
* Error response from the API
*/
export interface ErrorResponse {
message: string;
}
/**
* API call options extending the standard RequestInit
*/
export interface ApiCallOptions extends RequestInit {
headers?: HeadersInit;
}
/**
* Login request parameters
*/
export interface LoginRequest {
email: string;
password: string;
}
export interface LoginResponse {
user: User;
sessionId?: string;
expiresAt?: string; // ISO 8601 string representation of the date
}
export function isLoginResponse(obj: unknown): obj is LoginResponse {
return (
typeof obj === 'object' &&
obj !== null &&
'user' in obj &&
isUser(obj.user) &&
'sessionId' in obj &&
typeof (obj as LoginResponse).sessionId === 'string' &&
'expiresAt' in obj &&
typeof (obj as LoginResponse).expiresAt === 'string'
);
}
// CreateUserRequest holds the request fields for creating a new user
export interface CreateUserRequest {
email: string;
displayName: string;
password: string;
role: UserRole;
}
// UpdateUserRequest holds the request fields for updating a user
export interface UpdateUserRequest {
email?: string;
displayName?: string;
password?: string;
role?: UserRole;
}
export interface LookupResponse {
paths: string[];
}
export function isLookupResponse(obj: unknown): obj is LookupResponse {
return (
typeof obj === 'object' &&
obj !== null &&
'paths' in obj &&
Array.isArray((obj as LookupResponse).paths) &&
(obj as LookupResponse).paths.every((path) => typeof path === 'string')
);
}
export interface SaveFileResponse {
filePath: string;
size: number;
updatedAt: string; // ISO 8601 string representation of the date
}
export function isSaveFileResponse(obj: unknown): obj is SaveFileResponse {
return (
typeof obj === 'object' &&
obj !== null &&
'filePath' in obj &&
typeof (obj as SaveFileResponse).filePath === 'string' &&
'size' in obj &&
typeof (obj as SaveFileResponse).size === 'number' &&
'updatedAt' in obj &&
typeof (obj as SaveFileResponse).updatedAt === 'string'
);
}
export interface UpdateLastOpenedFileRequest {
filePath: string;
}
// UpdateProfileRequest represents a user profile update request
export interface UpdateProfileRequest {
displayName?: string;
email?: string;
currentPassword?: string;
newPassword?: string;
}
// DeleteAccountRequest represents a user account deletion request
export interface DeleteAccountRequest {
password: string;
}

View File

@@ -1,80 +0,0 @@
declare global {
interface Window {
API_BASE_URL: string;
}
}
export const API_BASE_URL = window.API_BASE_URL;
/**
* User role in the system
*/
export enum UserRole {
Admin = 'admin',
Editor = 'editor',
Viewer = 'viewer',
}
/**
* Type guard to check if a value is a valid UserRole
*/
export function isUserRole(value: unknown): value is UserRole {
return typeof value === 'string' && value in UserRole;
}
/**
* User model from the API
*/
export interface User {
id: number;
email: string;
displayName?: string;
role: UserRole;
createdAt: string;
lastWorkspaceId: number;
}
/**
* Type guard to check if a value is a valid User
*/
export function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
typeof (value as User).id === 'number' &&
'email' in value &&
typeof (value as User).email === 'string' &&
('displayName' in value
? typeof (value as User).displayName === 'string'
: true) &&
'role' in value &&
isUserRole((value as User).role) &&
'createdAt' in value &&
typeof (value as User).createdAt === 'string' &&
'lastWorkspaceId' in value &&
typeof (value as User).lastWorkspaceId === 'number'
);
}
/**
* Error response from the API
*/
export interface ErrorResponse {
message: string;
}
/**
* Login request parameters
*/
export interface LoginRequest {
email: string;
password: string;
}
/**
* API call options extending the standard RequestInit
*/
export interface ApiCallOptions extends RequestInit {
headers?: HeadersInit;
}

View File

@@ -1,37 +0,0 @@
export enum FileAction {
Create = 'create',
Update = 'update',
Delete = 'delete',
Rename = 'rename',
}
export enum FileExtension {
Markdown = '.md',
JPG = '.jpg',
JPEG = '.jpeg',
PNG = '.png',
GIF = '.gif',
WebP = '.webp',
SVG = '.svg',
}
export const IMAGE_EXTENSIONS = [
FileExtension.JPG,
FileExtension.JPEG,
FileExtension.PNG,
FileExtension.GIF,
FileExtension.WebP,
FileExtension.SVG,
];
export interface DefaultFile {
name: string;
path: string;
content: string;
}
export const DEFAULT_FILE: DefaultFile = {
name: 'New File.md',
path: 'New File.md',
content: '# Welcome to NovaMD\n\nStart editing here!',
};

View File

@@ -1,58 +0,0 @@
export interface LookupResponse {
paths: string[];
}
export function isLookupResponse(obj: unknown): obj is LookupResponse {
return (
typeof obj === 'object' &&
obj !== null &&
'paths' in obj &&
Array.isArray((obj as LookupResponse).paths) &&
(obj as LookupResponse).paths.every((path) => typeof path === 'string')
);
}
export interface SaveFileResponse {
filePath: string;
size: number;
updatedAt: string; // ISO 8601 string representation of the date
}
export function isSaveFileResponse(obj: unknown): obj is SaveFileResponse {
return (
typeof obj === 'object' &&
obj !== null &&
'filePath' in obj &&
typeof (obj as SaveFileResponse).filePath === 'string' &&
'size' in obj &&
typeof (obj as SaveFileResponse).size === 'number' &&
'updatedAt' in obj &&
typeof (obj as SaveFileResponse).updatedAt === 'string'
);
}
export interface UpdateLastOpenedFileRequest {
filePath: string;
}
export interface FileNode {
id: string;
name: string;
path: string;
children: FileNode[];
}
export function isFileNode(obj: unknown): obj is FileNode {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
typeof (obj as FileNode).id === 'string' &&
'name' in obj &&
typeof (obj as FileNode).name === 'string' &&
'path' in obj &&
typeof (obj as FileNode).path === 'string' &&
'children' in obj &&
Array.isArray((obj as FileNode).children)
);
}

View File

@@ -1 +0,0 @@
export type CommitHash = string;

View File

@@ -1,14 +0,0 @@
export enum InlineContainerType {
Paragraph = 'paragraph',
ListItem = 'listItem',
TableCell = 'tableCell',
Blockquote = 'blockquote',
Heading = 'heading',
Emphasis = 'emphasis',
Strong = 'strong',
Delete = 'delete',
}
export const MARKDOWN_REGEX = {
WIKILINK: /(!?)\[\[(.*?)\]\]/g,
} as const;

View File

@@ -1,5 +0,0 @@
export enum ModalType {
NewFile = 'newFile',
DeleteFile = 'deleteFile',
CommitMessage = 'commitMessage',
}

269
app/src/types/models.ts Normal file
View File

@@ -0,0 +1,269 @@
/**
* User model from the API
*/
export interface User {
id: number;
email: string;
displayName?: string;
role: UserRole;
createdAt: string;
lastWorkspaceId: number;
}
/**
* Type guard to check if a value is a valid User
*/
export function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
typeof (value as User).id === 'number' &&
'email' in value &&
typeof (value as User).email === 'string' &&
('displayName' in value
? typeof (value as User).displayName === 'string'
: true) &&
'role' in value &&
isUserRole((value as User).role) &&
'createdAt' in value &&
typeof (value as User).createdAt === 'string' &&
'lastWorkspaceId' in value &&
typeof (value as User).lastWorkspaceId === 'number'
);
}
/**
* User role in the system
*/
export enum UserRole {
Admin = 'admin',
Editor = 'editor',
Viewer = 'viewer',
}
/**
* Type guard to check if a value is a valid UserRole
*/
export function isUserRole(value: unknown): value is UserRole {
return typeof value === 'string' && value in UserRole;
}
export enum Theme {
Light = 'light',
Dark = 'dark',
}
export interface WorkspaceSettings {
theme: Theme;
autoSave: boolean;
showHiddenFiles: boolean;
gitEnabled: boolean;
gitUrl: string;
gitUser: string;
gitToken: string;
gitAutoCommit: boolean;
gitCommitMsgTemplate: string;
gitCommitName: string;
gitCommitEmail: string;
}
export const DEFAULT_WORKSPACE_SETTINGS: WorkspaceSettings = {
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '${action} ${filename}',
gitCommitName: '',
gitCommitEmail: '',
};
export interface Workspace extends WorkspaceSettings {
name: string;
createdAt: number;
}
export const DEFAULT_WORKSPACE: Workspace = {
name: '',
createdAt: Date.now(),
...DEFAULT_WORKSPACE_SETTINGS,
};
export function isWorkspace(obj: unknown): obj is Workspace {
return (
typeof obj === 'object' &&
obj !== null &&
'name' in obj &&
typeof (obj as Workspace).name === 'string' &&
'theme' in obj &&
typeof (obj as Workspace).theme === 'string' &&
'autoSave' in obj &&
typeof (obj as Workspace).autoSave === 'boolean' &&
'gitEnabled' in obj &&
typeof (obj as Workspace).gitEnabled === 'boolean' &&
'gitUrl' in obj &&
typeof (obj as Workspace).gitUrl === 'string' &&
'gitUser' in obj &&
typeof (obj as Workspace).gitUser === 'string' &&
'gitToken' in obj &&
typeof (obj as Workspace).gitToken === 'string' &&
'gitAutoCommit' in obj &&
typeof (obj as Workspace).gitAutoCommit === 'boolean' &&
'gitCommitMsgTemplate' in obj &&
typeof (obj as Workspace).gitCommitMsgTemplate === 'string'
);
}
export enum FileAction {
Create = 'create',
Update = 'update',
Delete = 'delete',
Rename = 'rename',
}
export enum FileExtension {
Markdown = '.md',
JPG = '.jpg',
JPEG = '.jpeg',
PNG = '.png',
GIF = '.gif',
WebP = '.webp',
SVG = '.svg',
}
export const IMAGE_EXTENSIONS = [
FileExtension.JPG,
FileExtension.JPEG,
FileExtension.PNG,
FileExtension.GIF,
FileExtension.WebP,
FileExtension.SVG,
];
export interface DefaultFile {
name: string;
path: string;
content: string;
}
export const DEFAULT_FILE: DefaultFile = {
name: 'New File.md',
path: 'New File.md',
content: '# Welcome to NovaMD\n\nStart editing here!',
};
export interface FileNode {
id: string;
name: string;
path: string;
children: FileNode[];
}
export function isFileNode(obj: unknown): obj is FileNode {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
typeof (obj as FileNode).id === 'string' &&
'name' in obj &&
typeof (obj as FileNode).name === 'string' &&
'path' in obj &&
typeof (obj as FileNode).path === 'string' &&
'children' in obj &&
Array.isArray((obj as FileNode).children)
);
}
// WorkspaceStats holds workspace statistics
export interface WorkspaceStats {
userID: number;
userEmail: string;
workspaceID: number;
workspaceName: string;
workspaceCreatedAt: string; // Using ISO string format for time.Time
fileCountStats?: FileCountStats;
}
// Define FileCountStats based on the Go struct definition of storage.FileCountStats
export interface FileCountStats {
totalFiles: number;
totalSize: number;
}
export interface UserStats {
totalUsers: number;
totalWorkspaces: number;
activeUsers: number; // Users with activity in last 30 days
}
// SystemStats holds system-wide statistics
export interface SystemStats extends FileCountStats, UserStats {}
// isSystemStats checks if the given object is a valid SystemStats object
export function isSystemStats(obj: unknown): obj is SystemStats {
return (
typeof obj === 'object' &&
obj !== null &&
'totalUsers' in obj &&
typeof (obj as SystemStats).totalUsers === 'number' &&
'totalWorkspaces' in obj &&
typeof (obj as SystemStats).totalWorkspaces === 'number' &&
'activeUsers' in obj &&
typeof (obj as SystemStats).activeUsers === 'number' &&
'totalFiles' in obj &&
typeof (obj as SystemStats).totalFiles === 'number' &&
'totalSize' in obj &&
typeof (obj as SystemStats).totalSize === 'number'
);
}
export type CommitHash = string;
export enum InlineContainerType {
Paragraph = 'paragraph',
ListItem = 'listItem',
TableCell = 'tableCell',
Blockquote = 'blockquote',
Heading = 'heading',
Emphasis = 'emphasis',
Strong = 'strong',
Delete = 'delete',
}
export const MARKDOWN_REGEX = {
WIKILINK: /(!?)\[\[(.*?)\]\]/g,
} as const;
export enum ModalType {
NewFile = 'newFile',
DeleteFile = 'deleteFile',
CommitMessage = 'commitMessage',
}
export enum SettingsActionType {
INIT_SETTINGS = 'INIT_SETTINGS',
UPDATE_LOCAL_SETTINGS = 'UPDATE_LOCAL_SETTINGS',
MARK_SAVED = 'MARK_SAVED',
}
export interface UserProfileSettings {
displayName?: string;
email?: string;
currentPassword?: string;
newPassword?: string;
}
export interface ProfileSettingsState {
localSettings: UserProfileSettings;
initialSettings: UserProfileSettings;
hasUnsavedChanges: boolean;
}
export interface SettingsAction<T> {
type: SettingsActionType;
payload?: T;
}

View File

@@ -1,23 +0,0 @@
export enum SettingsActionType {
INIT_SETTINGS = 'INIT_SETTINGS',
UPDATE_LOCAL_SETTINGS = 'UPDATE_LOCAL_SETTINGS',
MARK_SAVED = 'MARK_SAVED',
}
export interface UserProfileSettings {
displayName?: string;
email?: string;
currentPassword?: string;
newPassword?: string;
}
export interface ProfileSettingsState {
localSettings: UserProfileSettings;
initialSettings: UserProfileSettings;
hasUnsavedChanges: boolean;
}
export interface SettingsAction<T> {
type: SettingsActionType;
payload?: T;
}

View File

@@ -1,4 +0,0 @@
export enum Theme {
Light = 'light',
Dark = 'dark',
}

View File

@@ -1,12 +0,0 @@
// UpdateProfileRequest represents a user profile update request
export interface UpdateProfileRequest {
displayName?: string;
email?: string;
currentPassword?: string;
newPassword?: string;
}
// DeleteAccountRequest represents a user account deletion request
export interface DeleteAccountRequest {
password: string;
}

View File

@@ -1,65 +0,0 @@
import { Theme } from './theme';
export interface WorkspaceSettings {
theme: Theme;
autoSave: boolean;
showHiddenFiles: boolean;
gitEnabled: boolean;
gitUrl: string;
gitUser: string;
gitToken: string;
gitAutoCommit: boolean;
gitCommitMsgTemplate: string;
gitCommitName: string;
gitCommitEmail: string;
}
export const DEFAULT_WORKSPACE_SETTINGS: WorkspaceSettings = {
theme: Theme.Light,
autoSave: false,
showHiddenFiles: false,
gitEnabled: false,
gitUrl: '',
gitUser: '',
gitToken: '',
gitAutoCommit: false,
gitCommitMsgTemplate: '${action} ${filename}',
gitCommitName: '',
gitCommitEmail: '',
};
export interface Workspace extends WorkspaceSettings {
name: string;
createdAt: number;
}
export const DEFAULT_WORKSPACE: Workspace = {
name: '',
createdAt: Date.now(),
...DEFAULT_WORKSPACE_SETTINGS,
};
export function isWorkspace(obj: unknown): obj is Workspace {
return (
typeof obj === 'object' &&
obj !== null &&
'name' in obj &&
typeof (obj as Workspace).name === 'string' &&
'theme' in obj &&
typeof (obj as Workspace).theme === 'string' &&
'autoSave' in obj &&
typeof (obj as Workspace).autoSave === 'boolean' &&
'gitEnabled' in obj &&
typeof (obj as Workspace).gitEnabled === 'boolean' &&
'gitUrl' in obj &&
typeof (obj as Workspace).gitUrl === 'string' &&
'gitUser' in obj &&
typeof (obj as Workspace).gitUser === 'string' &&
'gitToken' in obj &&
typeof (obj as Workspace).gitToken === 'string' &&
'gitAutoCommit' in obj &&
typeof (obj as Workspace).gitAutoCommit === 'boolean' &&
'gitCommitMsgTemplate' in obj &&
typeof (obj as Workspace).gitCommitMsgTemplate === 'string'
);
}