feat: support multiple database
This commit is contained in:
parent
1de829e192
commit
6e6dbb7959
@ -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
|
||||
|
@ -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"
|
||||
- "5000:80"
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
1236
package-lock.json
generated
1236
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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', () => {
|
42
src/storage/storage.service.ts
Normal file
42
src/storage/storage.service.ts
Normal 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',
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export interface StorageService {
|
||||
save(id: string, data: Buffer): Promise<boolean>;
|
||||
|
||||
load(id: string): Promise<Buffer | false>;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user