mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 23:44:22 +00:00
Refactor types
This commit is contained in:
@@ -1,15 +1,17 @@
|
|||||||
import { apiCall } from './api';
|
|
||||||
import type { User } from '../types/authApi';
|
|
||||||
import { API_BASE_URL, isUser } from '../types/authApi';
|
|
||||||
import type {
|
|
||||||
CreateUserRequest,
|
|
||||||
SystemStats,
|
|
||||||
UpdateUserRequest} from '@/types/adminApi';
|
|
||||||
import {
|
import {
|
||||||
isSystemStats
|
API_BASE_URL,
|
||||||
} from '@/types/adminApi';
|
type CreateUserRequest,
|
||||||
import type { Workspace } from '@/types/workspace';
|
type UpdateUserRequest,
|
||||||
import { isWorkspace } from '@/types/workspace';
|
} from '@/types/api';
|
||||||
|
import { apiCall } from './api';
|
||||||
|
import {
|
||||||
|
isSystemStats,
|
||||||
|
isUser,
|
||||||
|
isWorkspace,
|
||||||
|
type SystemStats,
|
||||||
|
type User,
|
||||||
|
type Workspace,
|
||||||
|
} from '@/types/models';
|
||||||
|
|
||||||
const ADMIN_BASE_URL = `${API_BASE_URL}/admin`;
|
const ADMIN_BASE_URL = `${API_BASE_URL}/admin`;
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ const ADMIN_BASE_URL = `${API_BASE_URL}/admin`;
|
|||||||
* */
|
* */
|
||||||
export const getUsers = async (): Promise<User[]> => {
|
export const getUsers = async (): Promise<User[]> => {
|
||||||
const response = await apiCall(`${ADMIN_BASE_URL}/users`);
|
const response = await apiCall(`${ADMIN_BASE_URL}/users`);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
|
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
throw new Error('Invalid users response received from API');
|
throw new Error('Invalid users response received from API');
|
||||||
@@ -49,7 +51,7 @@ export const createUser = async (
|
|||||||
body: JSON.stringify(userData),
|
body: JSON.stringify(userData),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!isUser(data)) {
|
if (!isUser(data)) {
|
||||||
throw new Error('Invalid user object received from API');
|
throw new Error('Invalid user object received from API');
|
||||||
}
|
}
|
||||||
@@ -88,7 +90,7 @@ export const updateUser = async (
|
|||||||
body: JSON.stringify(userData),
|
body: JSON.stringify(userData),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!isUser(data)) {
|
if (!isUser(data)) {
|
||||||
throw new Error('Invalid user object received from API');
|
throw new Error('Invalid user object received from API');
|
||||||
}
|
}
|
||||||
@@ -104,7 +106,7 @@ export const updateUser = async (
|
|||||||
* */
|
* */
|
||||||
export const getWorkspaces = async (): Promise<Workspace[]> => {
|
export const getWorkspaces = async (): Promise<Workspace[]> => {
|
||||||
const response = await apiCall(`${ADMIN_BASE_URL}/workspaces`);
|
const response = await apiCall(`${ADMIN_BASE_URL}/workspaces`);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
throw new Error('Invalid workspaces response received from API');
|
throw new Error('Invalid workspaces response received from API');
|
||||||
}
|
}
|
||||||
@@ -125,7 +127,7 @@ export const getWorkspaces = async (): Promise<Workspace[]> => {
|
|||||||
* */
|
* */
|
||||||
export const getSystemStats = async (): Promise<SystemStats> => {
|
export const getSystemStats = async (): Promise<SystemStats> => {
|
||||||
const response = await apiCall(`${ADMIN_BASE_URL}/stats`);
|
const response = await apiCall(`${ADMIN_BASE_URL}/stats`);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!isSystemStats(data)) {
|
if (!isSystemStats(data)) {
|
||||||
throw new Error('Invalid system stats response received from API');
|
throw new Error('Invalid system stats response received from API');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { User, LoginRequest} from '../types/authApi';
|
import { API_BASE_URL, isLoginResponse, type LoginRequest } from '@/types/api';
|
||||||
import { API_BASE_URL, isUser } from '../types/authApi';
|
|
||||||
import { apiCall } from './api';
|
import { apiCall } from './api';
|
||||||
|
import { isUser, type User } from '@/types/models';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs in a user with email and password
|
* Logs in a user with email and password
|
||||||
@@ -17,8 +17,8 @@ export const login = async (email: string, password: string): Promise<User> => {
|
|||||||
body: JSON.stringify(loginData),
|
body: JSON.stringify(loginData),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!('user' in data) || !isUser(data.user)) {
|
if (!isLoginResponse(data)) {
|
||||||
throw new Error('Invalid login response from API');
|
throw new Error('Invalid login response from API');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ export const refreshToken = async (): Promise<boolean> => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -65,7 +65,7 @@ export const refreshToken = async (): Promise<boolean> => {
|
|||||||
*/
|
*/
|
||||||
export const getCurrentUser = async (): Promise<User> => {
|
export const getCurrentUser = async (): Promise<User> => {
|
||||||
const response = await apiCall(`${API_BASE_URL}/auth/me`);
|
const response = await apiCall(`${API_BASE_URL}/auth/me`);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
|
|
||||||
if (!isUser(data)) {
|
if (!isUser(data)) {
|
||||||
throw new Error('Invalid user data received from API');
|
throw new Error('Invalid user data received from API');
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
import { API_BASE_URL } from '@/types/authApi';
|
import { isFileNode, type FileNode } from '@/types/models';
|
||||||
import { apiCall } from './api';
|
import { apiCall } from './api';
|
||||||
import type {
|
|
||||||
FileNode,
|
|
||||||
LookupResponse,
|
|
||||||
SaveFileResponse} from '@/types/fileApi';
|
|
||||||
import {
|
import {
|
||||||
isFileNode,
|
API_BASE_URL,
|
||||||
isLookupResponse,
|
isLookupResponse,
|
||||||
isSaveFileResponse
|
isSaveFileResponse,
|
||||||
} from '@/types/fileApi';
|
type SaveFileResponse,
|
||||||
|
} from '@/types/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* listFiles fetches the list of files in a workspace
|
* listFiles fetches the list of files in a workspace
|
||||||
@@ -20,7 +17,7 @@ export const listFiles = async (workspaceName: string): Promise<FileNode[]> => {
|
|||||||
const response = await apiCall(
|
const response = await apiCall(
|
||||||
`${API_BASE_URL}/workspaces/${encodeURIComponent(workspaceName)}/files`
|
`${API_BASE_URL}/workspaces/${encodeURIComponent(workspaceName)}/files`
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
throw new Error('Invalid files response received from API');
|
throw new Error('Invalid files response received from API');
|
||||||
}
|
}
|
||||||
@@ -48,7 +45,7 @@ export const lookupFileByName = async (
|
|||||||
workspaceName
|
workspaceName
|
||||||
)}/files/lookup?filename=${encodeURIComponent(filename)}`
|
)}/files/lookup?filename=${encodeURIComponent(filename)}`
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!isLookupResponse(data)) {
|
if (!isLookupResponse(data)) {
|
||||||
throw new Error('Invalid lookup response received from API');
|
throw new Error('Invalid lookup response received from API');
|
||||||
}
|
}
|
||||||
@@ -100,7 +97,7 @@ export const saveFile = async (
|
|||||||
body: content,
|
body: content,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!isSaveFileResponse(data)) {
|
if (!isSaveFileResponse(data)) {
|
||||||
throw new Error('Invalid save file response received from API');
|
throw new Error('Invalid save file response received from API');
|
||||||
}
|
}
|
||||||
@@ -136,11 +133,15 @@ export const getLastOpenedFile = async (
|
|||||||
const response = await apiCall(
|
const response = await apiCall(
|
||||||
`${API_BASE_URL}/workspaces/${encodeURIComponent(workspaceName)}/files/last`
|
`${API_BASE_URL}/workspaces/${encodeURIComponent(workspaceName)}/files/last`
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!('lastOpenedFilePath' in data)) {
|
if (
|
||||||
|
typeof data !== 'object' ||
|
||||||
|
data === null ||
|
||||||
|
!('lastOpenedFilePath' in data)
|
||||||
|
) {
|
||||||
throw new Error('Invalid last opened file response received from API');
|
throw new Error('Invalid last opened file response received from API');
|
||||||
}
|
}
|
||||||
return data.lastOpenedFilePath;
|
return data.lastOpenedFilePath as string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { API_BASE_URL } from '@/types/authApi';
|
import { API_BASE_URL } from '@/types/api';
|
||||||
import { apiCall } from './api';
|
import { apiCall } from './api';
|
||||||
import type { CommitHash } from '@/types/git';
|
import type { CommitHash } from '@/types/models';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pullChanges fetches the latest changes from the remote repository
|
* pullChanges fetches the latest changes from the remote repository
|
||||||
@@ -14,11 +14,11 @@ export const pullChanges = async (workspaceName: string): Promise<string> => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!('message' in data)) {
|
if (typeof data !== 'object' || data === null || !('message' in data)) {
|
||||||
throw new Error('Invalid pull response received from API');
|
throw new Error('Invalid pull response received from API');
|
||||||
}
|
}
|
||||||
return data.message;
|
return data.message as string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,8 +42,8 @@ export const commitAndPush = async (
|
|||||||
body: JSON.stringify({ message }),
|
body: JSON.stringify({ message }),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!('commitHash' in data)) {
|
if (typeof data !== 'object' || data === null || !('commitHash' in data)) {
|
||||||
throw new Error('Invalid commit response received from API');
|
throw new Error('Invalid commit response received from API');
|
||||||
}
|
}
|
||||||
return data.commitHash as CommitHash;
|
return data.commitHash as CommitHash;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { User } from '@/types/authApi';
|
import { API_BASE_URL, type UpdateProfileRequest } from '@/types/api';
|
||||||
import { API_BASE_URL, isUser } from '@/types/authApi';
|
import { isUser, type User } from '@/types/models';
|
||||||
import { apiCall } from './api';
|
import { apiCall } from './api';
|
||||||
import type { UpdateProfileRequest } from '@/types/userApi';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updateProfile updates the user's profile information.
|
* updateProfile updates the user's profile information.
|
||||||
@@ -16,12 +15,12 @@ export const updateProfile = async (
|
|||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify(updateRequest),
|
body: JSON.stringify(updateRequest),
|
||||||
});
|
});
|
||||||
const data = response.json();
|
const data: unknown = response.json();
|
||||||
|
|
||||||
if (!isUser(data)) {
|
if (!isUser(data)) {
|
||||||
throw new Error('Invalid user data');
|
throw new Error('Invalid user data');
|
||||||
}
|
}
|
||||||
return data as User;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { API_BASE_URL } from '@/types/authApi';
|
import { type Workspace, isWorkspace } from '@/types/models';
|
||||||
import { apiCall } from './api';
|
import { apiCall } from './api';
|
||||||
import type { Workspace } from '@/types/workspace';
|
import { API_BASE_URL } from '@/types/api';
|
||||||
import { isWorkspace } from '@/types/workspace';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* listWorkspaces fetches the list of workspaces
|
* listWorkspaces fetches the list of workspaces
|
||||||
@@ -10,7 +9,7 @@ import { isWorkspace } from '@/types/workspace';
|
|||||||
*/
|
*/
|
||||||
export const listWorkspaces = async (): Promise<Workspace[]> => {
|
export const listWorkspaces = async (): Promise<Workspace[]> => {
|
||||||
const response = await apiCall(`${API_BASE_URL}/workspaces`);
|
const response = await apiCall(`${API_BASE_URL}/workspaces`);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
throw new Error('Invalid workspaces response received from API');
|
throw new Error('Invalid workspaces response received from API');
|
||||||
}
|
}
|
||||||
@@ -36,7 +35,7 @@ export const createWorkspace = async (name: string): Promise<Workspace> => {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ name }),
|
body: JSON.stringify({ name }),
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!isWorkspace(data)) {
|
if (!isWorkspace(data)) {
|
||||||
throw new Error('Invalid workspace object received from API');
|
throw new Error('Invalid workspace object received from API');
|
||||||
}
|
}
|
||||||
@@ -105,8 +104,12 @@ export const deleteWorkspace = async (
|
|||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!('nextWorkspaceName' in data)) {
|
if (
|
||||||
|
typeof data !== 'object' ||
|
||||||
|
data === null ||
|
||||||
|
!('nextWorkspaceName' in data)
|
||||||
|
) {
|
||||||
throw new Error('Invalid delete workspace response received from API');
|
throw new Error('Invalid delete workspace response received from API');
|
||||||
}
|
}
|
||||||
return data.nextWorkspaceName as string;
|
return data.nextWorkspaceName as string;
|
||||||
@@ -119,8 +122,12 @@ export const deleteWorkspace = async (
|
|||||||
*/
|
*/
|
||||||
export const getLastWorkspaceName = async (): Promise<string> => {
|
export const getLastWorkspaceName = async (): Promise<string> => {
|
||||||
const response = await apiCall(`${API_BASE_URL}/workspaces/last`);
|
const response = await apiCall(`${API_BASE_URL}/workspaces/last`);
|
||||||
const data = await response.json();
|
const data: unknown = await response.json();
|
||||||
if (!('lastWorkspaceName' in data)) {
|
if (
|
||||||
|
typeof data !== 'object' ||
|
||||||
|
data === null ||
|
||||||
|
!('lastWorkspaceName' in data)
|
||||||
|
) {
|
||||||
throw new Error('Invalid last workspace name response received from API');
|
throw new Error('Invalid last workspace name response received from API');
|
||||||
}
|
}
|
||||||
return data.lastWorkspaceName as string;
|
return data.lastWorkspaceName as string;
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import type { ReactNode } from 'react';
|
import React, {
|
||||||
import React, { createContext, useContext, useState } from 'react';
|
type ReactNode,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
interface ModalContextType {
|
interface ModalContextType {
|
||||||
newFileModalVisible: boolean;
|
newFileModalVisible: boolean;
|
||||||
|
|||||||
@@ -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
116
app/src/types/api.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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!',
|
|
||||||
};
|
|
||||||
@@ -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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export type CommitHash = string;
|
|
||||||
@@ -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;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export enum ModalType {
|
|
||||||
NewFile = 'newFile',
|
|
||||||
DeleteFile = 'deleteFile',
|
|
||||||
CommitMessage = 'commitMessage',
|
|
||||||
}
|
|
||||||
269
app/src/types/models.ts
Normal file
269
app/src/types/models.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum Theme {
|
|
||||||
Light = 'light',
|
|
||||||
Dark = 'dark',
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user