Skip to content

Commit

Permalink
feat: add logs and refactor to organize logic
Browse files Browse the repository at this point in the history
  • Loading branch information
rboixaderg committed Nov 24, 2023
1 parent b793760 commit e886b9a
Show file tree
Hide file tree
Showing 28 changed files with 680 additions and 137 deletions.
1 change: 0 additions & 1 deletion electron-app/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
Expand Down
35 changes: 16 additions & 19 deletions electron-app/src/fileSystemFn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,20 @@ import { constants } from 'fs';
import fs from 'fs/promises';
import path from 'path';

export async function createFile(path: string, content: string | Uint8Array): Promise<void> {
try {
await fs.writeFile(path, content);
console.log(`File created at ${path}`);
} catch (error) {
console.error('Error creating file:', error);
}
export async function writeFile(path: string, content: string | Uint8Array): Promise<void> {
await fs.writeFile(path, content);
}

export async function appendFile(path: string, content: string | Uint8Array): Promise<void> {
await fs.appendFile(path, content);
}

export async function createDirectory(path: string): Promise<void> {
try {
await fs.mkdir(path, { recursive: true });
console.log(`Directory deleted at ${path}`);
} catch (error) {
console.error('Error delete directory:', error);
}
await fs.mkdir(path, { recursive: true });
}

export async function deleteDirectory(path: string): Promise<void> {
try {
await fs.rmdir(path, { recursive: true });
console.log(`Directory created at ${path}`);
} catch (error) {
console.error('Error creating directory:', error);
}
await fs.rmdir(path, { recursive: true });
}

export async function pathExists(path: string): Promise<boolean> {
Expand All @@ -38,6 +27,14 @@ export async function pathExists(path: string): Promise<boolean> {
}
}

export async function readFile(path: string) {
return await fs.readFile(path, 'utf-8');
}

export async function deleteFile(path: string) {
await fs.rm(path);
}

export async function findFilesInDirectory(directory: string, extensions: string[]): Promise<string[]> {
const result: string[] = [];

Expand Down
11 changes: 8 additions & 3 deletions electron-app/src/fileSystemServerFn.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createDirectory, createFile, pathExists } from './fileSystemFn';
import { createDirectory, pathExists, writeFile } from './fileSystemFn';

const defaultConfig = {
syncPeriod: 3600, // In seconds
Expand All @@ -13,12 +13,17 @@ export async function beforeStartServer(basePath: string) {
await createDirectory(`${basePath}/sync`);
}

if (!(await pathExists(`${basePath}/logs`))) {
await createDirectory(`${basePath}/logs`);
}

const configPath = `${basePath}/config.json`;
if (!(await pathExists(configPath))) {
await createFile(configPath, JSON.stringify(defaultConfig, null, 2));
await writeFile(configPath, JSON.stringify(defaultConfig, null, 2));
}

const syncPath = `${basePath}/sync.json`;
if (!(await pathExists(syncPath))) {
await createFile(syncPath, JSON.stringify({}, null, 2));
await writeFile(syncPath, JSON.stringify({}, null, 2));
}
}
8 changes: 8 additions & 0 deletions electron-app/src/logic/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class CustomError extends Error {
constructor(
public readonly message: string,
public readonly statusCode: number = 400,
) {
super(message);
}
}
7 changes: 7 additions & 0 deletions electron-app/src/logic/logs/domain/log.datasource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LogEntity } from './log.entity';

export abstract class ILogDatasource {
abstract saveLog(log: LogEntity): Promise<void>;
abstract getLogs(): Promise<LogEntity[]>;
abstract deleteLogs(): Promise<void>;
}
39 changes: 39 additions & 0 deletions electron-app/src/logic/logs/domain/log.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export enum LogSeverityLevel {
low = 'low',
medium = 'medium',
high = 'high',
}

export interface LogEntityOptions {
level: LogSeverityLevel;
message: string;
origin: string;
createdAt?: Date;
}

export class LogEntity {
public level: LogSeverityLevel; // Enum
public message: string;
public createdAt: Date;
public origin: string;

constructor(options: LogEntityOptions) {
const { message, level, origin, createdAt = new Date() } = options;
this.message = message;
this.level = level;
this.createdAt = createdAt;
this.origin = origin;
}

static fromJson = (json: string): LogEntity => {
json = json === '' ? '{}' : json;
const { message, level, createdAt, origin } = JSON.parse(json);
const log = new LogEntity({
message,
level,
createdAt: new Date(createdAt),
origin,
});
return log;
};
}
7 changes: 7 additions & 0 deletions electron-app/src/logic/logs/domain/log.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LogEntity } from './log.entity';

export abstract class ILogRepository {
abstract saveLog(log: LogEntity): Promise<void>;
abstract getLogs(): Promise<LogEntity[]>;
abstract deleteLogs(): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ILogRepository } from '../log.repository';

export interface DeleteLogsUseCase {
execute(): Promise<void>;
}

export class DeleteLogs implements DeleteLogsUseCase {
constructor(private readonly repository: ILogRepository) {}

execute(): Promise<void> {
return this.repository.deleteLogs();
}
}
14 changes: 14 additions & 0 deletions electron-app/src/logic/logs/domain/use-cases/get-logs.use-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { LogEntity } from '../log.entity';
import { ILogRepository } from '../log.repository';

export interface GetLogUseCase {
execute(): Promise<LogEntity[]>;
}

export class GetLogs implements GetLogUseCase {
constructor(private readonly repository: ILogRepository) {}

execute(): Promise<LogEntity[]> {
return this.repository.getLogs();
}
}
14 changes: 14 additions & 0 deletions electron-app/src/logic/logs/domain/use-cases/save-logs.use-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { LogEntity } from '../log.entity';
import { ILogRepository } from '../log.repository';

export interface SaveLogsUseCase {
execute(log: LogEntity): Promise<void>;
}

export class SaveLogs implements SaveLogsUseCase {
constructor(private readonly repository: ILogRepository) {}

execute(log: LogEntity): Promise<void> {
return this.repository.saveLog(log);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
appendFile,
createDirectory,
deleteFile,
findFilesInDirectory,
pathExists,
readFile,
writeFile,
} from '../../../fileSystemFn';
import { ILogDatasource } from '../domain/log.datasource';
import { LogEntity } from '../domain/log.entity';

export class FileSystemLogDatasource implements ILogDatasource {
private basePath: string;

constructor(basePath: string) {
this.basePath = `${basePath}/logs`;
}

private createLogsFiles = async () => {
if (await pathExists(this.basePath)) {
await createDirectory(this.basePath);
}

const dateText = new Intl.DateTimeFormat('en-ca', { dateStyle: 'short' }).format(new Date());
const fileName = `${this.basePath}/${dateText}.log`;
if (!(await pathExists(`${this.basePath}/${dateText}.log`))) {
await writeFile(`${this.basePath}/${dateText}.log`, '');
}
return fileName;
};

async saveLog(newLog: LogEntity): Promise<void> {
const logAsJson = `${JSON.stringify(newLog)}\n`;
const fileName = await this.createLogsFiles();
await appendFile(fileName, logAsJson);
}

private getLogsFromFile = async (path: string): Promise<LogEntity[]> => {
const content = await readFile(path);
if (content === '') return [];

const logs = content.split('\n').map(LogEntity.fromJson);
return logs;
};

async getLogs(): Promise<LogEntity[]> {
const files = await findFilesInDirectory(this.basePath, ['log']);
const result = [];
for (const file of files) {
const logs = await this.getLogsFromFile(file);
result.push(...logs);
}
return result;
}

async deleteLogs(): Promise<void> {
const files = await findFilesInDirectory(this.basePath, ['log']);
for (const file of files) {
await deleteFile(file);
}
}
}
19 changes: 19 additions & 0 deletions electron-app/src/logic/logs/infrastructure/log.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ILogDatasource } from '../domain/log.datasource';
import { LogEntity } from '../domain/log.entity';
import { ILogRepository } from '../domain/log.repository';

export class LogRepository implements ILogRepository {
constructor(private readonly logDatasource: ILogDatasource) {}

async saveLog(log: LogEntity): Promise<void> {
return this.logDatasource.saveLog(log);
}

async getLogs(): Promise<LogEntity[]> {
return this.logDatasource.getLogs();
}

async deleteLogs(): Promise<void> {
return this.logDatasource.deleteLogs();
}
}
55 changes: 55 additions & 0 deletions electron-app/src/logic/logs/presentation/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Response, Router } from 'express';
import { CustomError } from '../../errors';
import { DeleteLogs } from '../domain/use-cases/delete-logs.use-case';
import { GetLogs } from '../domain/use-cases/get-logs.use-case';
import { FileSystemLogDatasource } from '../infrastructure/file-system.log.datasource';
import { LogRepository } from '../infrastructure/log.repository';

export class LogsFileSystemRoutes {
private readonly basePath: string;

constructor(basePath: string) {
this.basePath = basePath;
}

private handleError = (res: Response, error: unknown) => {
console.error(error);
if (error instanceof CustomError) {
res.status(error.statusCode).json({ error: error.message });
return;
}
res.status(500).json({ error: 'Internal server error' });
};

getRoutes(): Router {
const router = Router();
const datasource = new FileSystemLogDatasource(this.basePath);
const logRepository = new LogRepository(datasource);

router.get('/', async (_req, res) => {
try {
const data = await new GetLogs(logRepository).execute();
res.status(200).send(data);
} catch (error) {
this.handleError(res, error);
}
});

router.get('/:since', async (_req, res) => {
res.status(200).send([]);
});

router.delete('/', async (_req, res) => {
try {
await new DeleteLogs(logRepository).execute();
res.status(200).send({
message: 'Logs deleted',
});
} catch (error) {
this.handleError(res, error);
}
});

return router;
}
}
9 changes: 9 additions & 0 deletions electron-app/src/logic/sync/domain/sync.datasource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SyncEntity } from './sync.entity';

export abstract class ISyncDatasource {
abstract createSync(sync: SyncEntity): Promise<void>;
abstract getAllSync(): Promise<{ [id: string]: SyncEntity }>;
abstract getSync(id: string): Promise<SyncEntity | null>;
abstract deleteSync(id: string): Promise<void>;
abstract updateSync(id: string, sync: SyncEntity): Promise<void>;
}
Loading

0 comments on commit e886b9a

Please sign in to comment.