diff --git a/teammapper-backend/src/jobs/seedMapData.job.ts b/teammapper-backend/src/jobs/seedMapData.job.ts index 536cae68..21f3c408 100644 --- a/teammapper-backend/src/jobs/seedMapData.job.ts +++ b/teammapper-backend/src/jobs/seedMapData.job.ts @@ -40,6 +40,7 @@ const createMap = (nodes: IMmpClientNode[]): IMmpClientMap => { return { uuid: crypto.randomUUID(), lastModified: new Date(), + createdAt: new Date(), deleteAfterDays: 30, data: nodes, deletedAt: new Date(), diff --git a/teammapper-backend/src/map/entities/mmpMap.entity.ts b/teammapper-backend/src/map/entities/mmpMap.entity.ts index e307c08f..92f9afcc 100644 --- a/teammapper-backend/src/map/entities/mmpMap.entity.ts +++ b/teammapper-backend/src/map/entities/mmpMap.entity.ts @@ -36,4 +36,7 @@ export class MmpMap { }) /* eslint-enable @typescript-eslint/no-unused-vars */ nodes: MmpNode[] + + @Column({ type: 'timestamptz', default: () => 'now()' }) + createdAt: Date } diff --git a/teammapper-backend/src/map/entities/mmpNode.entity.ts b/teammapper-backend/src/map/entities/mmpNode.entity.ts index bb38cddd..184dfe3d 100644 --- a/teammapper-backend/src/map/entities/mmpNode.entity.ts +++ b/teammapper-backend/src/map/entities/mmpNode.entity.ts @@ -102,4 +102,7 @@ export class MmpNode { @Column({ type: 'timestamptz', nullable: true, default: () => 'now()' }) lastModified: Date + + @Column({ type: 'timestamptz', default: () => 'now()' }) + createdAt: Date } diff --git a/teammapper-backend/src/map/services/maps.service.spec.ts b/teammapper-backend/src/map/services/maps.service.spec.ts index ab6b27e2..ed6d9dbb 100644 --- a/teammapper-backend/src/map/services/maps.service.spec.ts +++ b/teammapper-backend/src/map/services/maps.service.spec.ts @@ -57,6 +57,7 @@ describe('MapsController', () => { coordinatesX: 3, coordinatesY: 1, lastModified: lastModified, + createdAt: new Date() }) } diff --git a/teammapper-backend/src/map/services/maps.service.ts b/teammapper-backend/src/map/services/maps.service.ts index 00aaa819..a514523e 100644 --- a/teammapper-backend/src/map/services/maps.service.ts +++ b/teammapper-backend/src/map/services/maps.service.ts @@ -27,10 +27,10 @@ export class MapsService { private nodesRepository: Repository, @InjectRepository(MmpMap) private mapsRepository: Repository - ) {} + ) { } findMap(uuid: string): Promise { - if(!uuidValidate(uuid)) return Promise.reject(new MalformedUUIDError('Invalid UUID')) + if (!uuidValidate(uuid)) return Promise.reject(new MalformedUUIDError('Invalid UUID')) return this.mapsRepository.findOne({ where: { id: uuid }, @@ -46,7 +46,7 @@ export class MapsService { const nodes: MmpNode[] = await this.findNodes(map?.id) const days: number = configService.deleteAfterDays() - + return mapMmpMapToClient( map, nodes, @@ -136,6 +136,7 @@ export class MapsService { ...existingNode, ...mapClientNodeToMmpNode(clientNode, mapId), lastModified: new Date(), + createdAt: new Date() }) } @@ -213,7 +214,7 @@ export class MapsService { return copyDate } - async deleteOutdatedMaps(afterDays: number = 30): Promise { + async deleteOutdatedMaps(afterDays: number = 30): Promise { const today = new Date() const deleteQuery = this.mapsRepository @@ -271,7 +272,7 @@ export class MapsService { async validatesNodeParentForNode( mapId: string, - node: MmpNode + node: MmpNode ): Promise { return ( node.root || diff --git a/teammapper-backend/src/map/types.ts b/teammapper-backend/src/map/types.ts index a5b0b60b..c95163f2 100644 --- a/teammapper-backend/src/map/types.ts +++ b/teammapper-backend/src/map/types.ts @@ -27,7 +27,8 @@ export interface IMmpClientMap { deleteAfterDays: number deletedAt: Date data: IMmpClientNode[] - options: IMmpClientMapOptions + options: IMmpClientMapOptions, + createdAt: Date } export interface IMmpClientPrivateMap { diff --git a/teammapper-backend/src/map/utils/clientServerMapping.ts b/teammapper-backend/src/map/utils/clientServerMapping.ts index 0a47d486..0f7bc6de 100644 --- a/teammapper-backend/src/map/utils/clientServerMapping.ts +++ b/teammapper-backend/src/map/utils/clientServerMapping.ts @@ -50,6 +50,7 @@ const mapMmpMapToClient = ( deletedAt, lastModified: serverMap.lastModified, options: serverMap?.options, + createdAt: serverMap.createdAt } } diff --git a/teammapper-backend/src/map/utils/tests/mapFactories.ts b/teammapper-backend/src/map/utils/tests/mapFactories.ts index a55d701f..d1ae7be5 100644 --- a/teammapper-backend/src/map/utils/tests/mapFactories.ts +++ b/teammapper-backend/src/map/utils/tests/mapFactories.ts @@ -14,6 +14,7 @@ export const createMmpMap = (overrides = {}): MmpMap => ({ fontMinSize: 1, fontIncrement: 1 }, + createdAt: new Date('1970-01-01'), nodes: Array(), ...overrides }) @@ -29,6 +30,7 @@ export const createMmpClientMap = (overrides = {}): IMmpClientMap => ({ fontMinSize: 1, fontIncrement: 1 }, + createdAt: new Date('1970-01-01'), ...overrides, }) diff --git a/teammapper-backend/src/migrations/1724314314717-AddCreatedAtToMap.ts b/teammapper-backend/src/migrations/1724314314717-AddCreatedAtToMap.ts new file mode 100644 index 00000000..7e6b5fa7 --- /dev/null +++ b/teammapper-backend/src/migrations/1724314314717-AddCreatedAtToMap.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AddCreatedAtToMap1724314314717 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "mmp_map" ADD "createdAt" TIMESTAMP WITH TIME ZONE` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mmp_map" DROP COLUMN "createdAt"`) + } + +} diff --git a/teammapper-backend/src/migrations/1724314435583-AddCreatedAtToNode.ts b/teammapper-backend/src/migrations/1724314435583-AddCreatedAtToNode.ts new file mode 100644 index 00000000..334808f9 --- /dev/null +++ b/teammapper-backend/src/migrations/1724314435583-AddCreatedAtToNode.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AddCreatedAtToNode1724314435583 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "mmp_node" ADD "createdAt" TIMESTAMP WITH TIME ZONE` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mmp_node" DROP COLUMN "createdAt"`) + } + +} diff --git a/teammapper-backend/src/migrations/1724325535133-AddDefaultToCreatedAtMmpMap.ts b/teammapper-backend/src/migrations/1724325535133-AddDefaultToCreatedAtMmpMap.ts new file mode 100644 index 00000000..b8c5ce3f --- /dev/null +++ b/teammapper-backend/src/migrations/1724325535133-AddDefaultToCreatedAtMmpMap.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddDefaultToCreatedAtMmpMap1724325535133 implements MigrationInterface { + name = 'AddDefaultToCreatedAtMmpMap1724325535133' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mmp_map" ALTER COLUMN "createdAt" SET DEFAULT now()`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mmp_map" ALTER COLUMN "createdAt" DROP DEFAULT`); + } + +} diff --git a/teammapper-backend/src/migrations/1724325567562-AddDefaultToCreatedAtMmpNode.ts b/teammapper-backend/src/migrations/1724325567562-AddDefaultToCreatedAtMmpNode.ts new file mode 100644 index 00000000..5db948ee --- /dev/null +++ b/teammapper-backend/src/migrations/1724325567562-AddDefaultToCreatedAtMmpNode.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddDefaultToCreatedAtMmpNode1724325567562 implements MigrationInterface { + name = 'AddDefaultToCreatedAtMmpNode1724325567562' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mmp_node" ALTER COLUMN "createdAt" SET DEFAULT now()`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mmp_node" ALTER COLUMN "createdAt" DROP DEFAULT`); + } + +} diff --git a/teammapper-backend/test/app.e2e-spec.ts b/teammapper-backend/test/app.e2e-spec.ts index 67697086..cd1acafa 100644 --- a/teammapper-backend/test/app.e2e-spec.ts +++ b/teammapper-backend/test/app.e2e-spec.ts @@ -95,6 +95,7 @@ describe('AppController (e2e)', () => { deleteAfterDays: 30, deletedAt: new Date(), options: { fontMaxSize: 10, fontMinSize: 15, fontIncrement: 2 }, + createdAt: new Date(), data: [ { name: 'test', diff --git a/teammapper-frontend/mmp/src/map/types.ts b/teammapper-frontend/mmp/src/map/types.ts index f84011c4..c1d349d3 100644 --- a/teammapper-frontend/mmp/src/map/types.ts +++ b/teammapper-frontend/mmp/src/map/types.ts @@ -9,6 +9,7 @@ interface MapCreateEvent { interface MapProperties { uuid: string, lastModified: number, + createdAt: number, data: MapSnapshot, deletedAt: number, deleteAfterDays: number diff --git a/teammapper-frontend/src/app/core/services/map-sync/map-sync.service.ts b/teammapper-frontend/src/app/core/services/map-sync/map-sync.service.ts index 47b34377..602128ee 100644 --- a/teammapper-frontend/src/app/core/services/map-sync/map-sync.service.ts +++ b/teammapper-frontend/src/app/core/services/map-sync/map-sync.service.ts @@ -105,6 +105,7 @@ export class MapSyncService implements OnDestroy { modificationSecret: privateServerMap.modificationSecret, ttl: serverMap.deletedAt, rootName: serverMap.data[0].name, + createdAt: serverMap.createdAt, }); this.prepareMap(serverMap); @@ -191,6 +192,7 @@ export class MapSyncService implements OnDestroy { const cachedMap: CachedMap = { data: this.mmpService.exportAsJSON(), lastModified: Date.now(), + createdAt: cachedMapEntry.cachedMap.createdAt, uuid: cachedMapEntry.cachedMap.uuid, deletedAt: cachedMapEntry.cachedMap.deletedAt, deleteAfterDays: cachedMapEntry.cachedMap.deleteAfterDays, @@ -350,6 +352,7 @@ export class MapSyncService implements OnDestroy { return Object.assign({}, serverMap, { lastModified: Date.parse(serverMap.lastModified), deletedAt: Date.parse(serverMap.deletedAt), + createdAt: Date.parse(serverMap.createdAt), }); } diff --git a/teammapper-frontend/src/app/core/services/map-sync/server-types.ts b/teammapper-frontend/src/app/core/services/map-sync/server-types.ts index 41f7f903..5588969a 100644 --- a/teammapper-frontend/src/app/core/services/map-sync/server-types.ts +++ b/teammapper-frontend/src/app/core/services/map-sync/server-types.ts @@ -39,6 +39,7 @@ interface ServerMap { deleteAfterDays: number; data: MapSnapshot; options: CachedMapOptions; + createdAt: string; } interface PrivateServerMap { diff --git a/teammapper-frontend/src/app/shared/models/cached-map.model.ts b/teammapper-frontend/src/app/shared/models/cached-map.model.ts index e9cba117..e65b27b7 100644 --- a/teammapper-frontend/src/app/shared/models/cached-map.model.ts +++ b/teammapper-frontend/src/app/shared/models/cached-map.model.ts @@ -7,6 +7,7 @@ export interface CachedMapEntry { export interface CachedMap { lastModified: number; + createdAt: number; data: MapSnapshot; uuid: string; deleteAfterDays: number;