feat: support multiple database
This commit is contained in:
parent
1de829e192
commit
6e6dbb7959
@ -1,2 +1,9 @@
|
|||||||
# excalidraw-storage-backend
|
# 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:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
environment:
|
environment:
|
||||||
|
BACKEND_V1_GET_URL: ' '
|
||||||
BACKEND_V2_GET_URL: http://localhost:8080/api/v2/
|
BACKEND_V2_GET_URL: http://localhost:8080/api/v2/
|
||||||
BACKEND_V2_POST_URL: http://localhost:8080/api/v2/
|
BACKEND_V2_POST_URL: http://localhost:8080/api/v2/
|
||||||
SOCKET_SERVER_URL: http://localhost:5000/
|
SOCKET_SERVER_URL: http://localhost:5000/
|
||||||
@ -16,8 +17,15 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
STORAGE_URI: redis://redis:6379
|
||||||
|
|
||||||
excalidraw-room:
|
excalidraw-room:
|
||||||
image: excalidraw/excalidraw-room
|
image: excalidraw/excalidraw-room
|
||||||
ports:
|
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"
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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/common": "^8.0.0",
|
||||||
"@nestjs/core": "^8.0.0",
|
"@nestjs/core": "^8.0.0",
|
||||||
"@nestjs/platform-express": "^8.0.0",
|
"@nestjs/platform-express": "^8.0.0",
|
||||||
|
"@types/keyv": "^3.1.3",
|
||||||
|
"keyv": "^4.0.3",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "^7.2.0"
|
"rxjs": "^7.2.0"
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
||||||
import { RawParserMiddleware } from './raw-parser.middleware';
|
import { RawParserMiddleware } from './raw-parser.middleware';
|
||||||
import { ScenesController } from './scenes/scenes.controller';
|
import { ScenesController } from './scenes/scenes.controller';
|
||||||
import { MemoryService } from './storages/memory.service';
|
import { StorageService } from './storage/storage.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [],
|
||||||
controllers: [ScenesController],
|
controllers: [ScenesController],
|
||||||
providers: [MemoryService],
|
providers: [StorageService],
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
configure(consumer: MiddlewareConsumer) {
|
configure(consumer: MiddlewareConsumer) {
|
||||||
|
@ -8,17 +8,19 @@ import {
|
|||||||
Res,
|
Res,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Response } from 'express';
|
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 { hash, hexadecimalToDecimal } from 'src/utils';
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class ScenesController {
|
export class ScenesController {
|
||||||
constructor(private storageService: MemoryService) {}
|
namespace = StorageNamespace.SCENES;
|
||||||
|
|
||||||
|
constructor(private storageService: StorageService) {}
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@Header('content-type', 'application/octet-stream')
|
@Header('content-type', 'application/octet-stream')
|
||||||
async findOne(@Param() params, @Res() res: Response): Promise<void> {
|
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();
|
const stream = new Readable();
|
||||||
stream.push(data);
|
stream.push(data);
|
||||||
@ -28,11 +30,10 @@ export class ScenesController {
|
|||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
async create(@Body() payload: Buffer) {
|
async create(@Body() payload: Buffer) {
|
||||||
|
|
||||||
const drawingHash = hash(payload);
|
const drawingHash = hash(payload);
|
||||||
const id = hexadecimalToDecimal(drawingHash);
|
const id = hexadecimalToDecimal(drawingHash);
|
||||||
|
|
||||||
await this.storageService.save(id, payload);
|
await this.storageService.set(id, payload, this.namespace);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { MemoryService } from './memory.service';
|
import { StorageService } from './storage.service';
|
||||||
|
|
||||||
describe('MemoryService', () => {
|
describe('StorageService', () => {
|
||||||
let service: MemoryService;
|
let service: StorageService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [MemoryService],
|
providers: [StorageService],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<MemoryService>(MemoryService);
|
service = module.get<StorageService>(StorageService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
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