feat: support multiple database

This commit is contained in:
Kilian Decaderincourt 2021-09-08 15:31:02 +02:00
parent 1de829e192
commit 6e6dbb7959
10 changed files with 1302 additions and 46 deletions

View File

@ -1,2 +1,9 @@
# excalidraw-storage-backend
## Environement Variables
| Name | Description | Default value |
| --- | --- | --- |
| `STORAGE_URI` | [keyv](https://github.com/lukechilds/keyv) connection string | `""` (in memory) |
Availabe storage adapter: redis, mongo, postgres and mysql

View File

@ -8,6 +8,7 @@ services:
ports:
- "80:80"
environment:
BACKEND_V1_GET_URL: ' '
BACKEND_V2_GET_URL: http://localhost:8080/api/v2/
BACKEND_V2_POST_URL: http://localhost:8080/api/v2/
SOCKET_SERVER_URL: http://localhost:5000/
@ -16,8 +17,15 @@ services:
build: .
ports:
- "8080:8080"
environment:
STORAGE_URI: redis://redis:6379
excalidraw-room:
image: excalidraw/excalidraw-room
ports:
- "5000:80"
redis:
image: redis
ports:
- "6379:6379"

1236
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,9 +21,15 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@keyv/mongo": "^1.1.0",
"@keyv/mysql": "^1.1.4",
"@keyv/postgres": "^1.0.16",
"@keyv/redis": "^2.1.2",
"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/platform-express": "^8.0.0",
"@types/keyv": "^3.1.3",
"keyv": "^4.0.3",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0"

View File

@ -1,12 +1,12 @@
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { RawParserMiddleware } from './raw-parser.middleware';
import { ScenesController } from './scenes/scenes.controller';
import { MemoryService } from './storages/memory.service';
import { StorageService } from './storage/storage.service';
@Module({
imports: [],
controllers: [ScenesController],
providers: [MemoryService],
providers: [StorageService],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {

View File

@ -8,17 +8,19 @@ import {
Res,
} from '@nestjs/common';
import { Response } from 'express';
import { MemoryService } from 'src/storages/memory.service';
import { StorageNamespace, StorageService } from 'src/storage/storage.service';
import { hash, hexadecimalToDecimal } from 'src/utils';
import { Readable } from 'stream';
@Controller()
export class ScenesController {
constructor(private storageService: MemoryService) {}
namespace = StorageNamespace.SCENES;
constructor(private storageService: StorageService) {}
@Get(':id')
@Header('content-type', 'application/octet-stream')
async findOne(@Param() params, @Res() res: Response): Promise<void> {
const data = await this.storageService.load(params.id);
const data = await this.storageService.get(params.id, this.namespace);
const stream = new Readable();
stream.push(data);
@ -28,11 +30,10 @@ export class ScenesController {
@Post()
async create(@Body() payload: Buffer) {
const drawingHash = hash(payload);
const id = hexadecimalToDecimal(drawingHash);
await this.storageService.save(id, payload);
await this.storageService.set(id, payload, this.namespace);
return {
id,

View File

@ -1,15 +1,15 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MemoryService } from './memory.service';
import { StorageService } from './storage.service';
describe('MemoryService', () => {
let service: MemoryService;
describe('StorageService', () => {
let service: StorageService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [MemoryService],
providers: [StorageService],
}).compile();
service = module.get<MemoryService>(MemoryService);
service = module.get<StorageService>(StorageService);
});
it('should be defined', () => {

View File

@ -0,0 +1,42 @@
import { Injectable } from '@nestjs/common';
import * as Keyv from 'keyv';
@Injectable()
export class StorageService {
storagesMap = new Map<string, Keyv>();
constructor() {
const uri = process.env[`STORAGE_URI`];
if (!uri) {
console.error(
`STORAGE_URI is undefined, will use non persistant in memory storage`,
);
}
Object.keys(StorageNamespace).forEach((namespace) => {
const keyv = new Keyv({
uri,
namespace,
});
keyv.on('error', (err) =>
console.error(`Connection Error for namespace ${namespace}`, err),
);
this.storagesMap.set(namespace, keyv);
});
}
get(key: string, namespace: StorageNamespace): Promise<Buffer> {
return this.storagesMap.get(namespace).get(key);
}
async has(key: string, namespace: StorageNamespace): Promise<boolean> {
return !!(await this.storagesMap.get(namespace).get(key));
}
set(key: string, value: Buffer, namespace: StorageNamespace): Promise<true> {
return this.storagesMap.get(namespace).set(key, value);
}
}
export enum StorageNamespace {
SCENES = 'SCENES',
ROOMS = 'ROOMS',
}

View File

@ -1,17 +0,0 @@
import { Injectable } from '@nestjs/common';
import { StorageService } from './storageService';
@Injectable()
export class MemoryService implements StorageService {
scenesMap = new Map<string, Buffer>();
async save(id: string, data: Buffer): Promise<boolean> {
this.scenesMap.set(id, data);
return true;
}
async load(id: string): Promise<false | Buffer> {
return this.scenesMap.get(id);
}
}

View File

@ -1,5 +0,0 @@
export interface StorageService {
save(id: string, data: Buffer): Promise<boolean>;
load(id: string): Promise<Buffer | false>;
}