feat: add shared vaults model. (#631)
Co-authored-by: Mo <mo@standardnotes.com>
This commit is contained in:
parent
060206ddd4
commit
38e77f04be
14 changed files with 209 additions and 3 deletions
3
.pnp.cjs
generated
3
.pnp.cjs
generated
|
@ -5179,6 +5179,7 @@ const RAW_RUNTIME_STATE =
|
|||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
|
@ -5188,6 +5189,7 @@ const RAW_RUNTIME_STATE =
|
|||
["@types/newrelic", "npm:9.14.0"],\
|
||||
["@types/node", "npm:20.2.5"],\
|
||||
["@types/prettyjson", "npm:0.0.30"],\
|
||||
["@types/semver", "npm:7.5.0"],\
|
||||
["@types/ua-parser-js", "npm:0.7.36"],\
|
||||
["@types/uuid", "npm:8.3.4"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
|
||||
|
@ -5210,6 +5212,7 @@ const RAW_RUNTIME_STATE =
|
|||
["prettier", "npm:2.8.8"],\
|
||||
["prettyjson", "npm:1.2.5"],\
|
||||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["semver", "npm:7.5.1"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.16"],\
|
||||
|
|
|
@ -20,7 +20,7 @@ export class Dates extends ValueObject<DatesProps> {
|
|||
return Result.fail<Dates>(`Could not create Dates. Creation date should be a date object, given: ${createdAt}`)
|
||||
}
|
||||
if (!(updatedAt instanceof Date)) {
|
||||
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${createdAt}`)
|
||||
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${updatedAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Dates>(new Dates({ createdAt, updatedAt }))
|
||||
|
|
30
packages/domain-core/src/Domain/Common/Timestamps.ts
Normal file
30
packages/domain-core/src/Domain/Common/Timestamps.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { TimestampsProps } from './TimestampsProps'
|
||||
|
||||
export class Timestamps extends ValueObject<TimestampsProps> {
|
||||
get createdAt(): number {
|
||||
return this.props.createdAt
|
||||
}
|
||||
|
||||
get updatedAt(): number {
|
||||
return this.props.updatedAt
|
||||
}
|
||||
|
||||
private constructor(props: TimestampsProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(createdAt: number, updatedAt: number): Result<Timestamps> {
|
||||
if (isNaN(createdAt)) {
|
||||
return Result.fail<Timestamps>(
|
||||
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
|
||||
)
|
||||
}
|
||||
if (isNaN(updatedAt)) {
|
||||
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${updatedAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Timestamps>(new Timestamps({ createdAt, updatedAt }))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export interface TimestampsProps {
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
|
@ -17,6 +17,8 @@ export * from './Common/RoleName'
|
|||
export * from './Common/RoleNameProps'
|
||||
export * from './Common/RoleNameCollection'
|
||||
export * from './Common/RoleNameCollectionProps'
|
||||
export * from './Common/Timestamps'
|
||||
export * from './Common/TimestampsProps'
|
||||
export * from './Common/Username'
|
||||
export * from './Common/UsernameProps'
|
||||
export * from './Common/Uuid'
|
||||
|
|
|
@ -20,6 +20,9 @@ DB_TYPE=mysql
|
|||
REDIS_URL=redis://cache
|
||||
CACHE_TYPE=redis
|
||||
|
||||
VALET_TOKEN_SECRET=change-me-!
|
||||
VALET_TOKEN_TTL=1000
|
||||
|
||||
SNS_TOPIC_ARN=
|
||||
SNS_AWS_REGION=
|
||||
SQS_QUEUE_URL=
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"content-size": "yarn node dist/bin/content.js",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'",
|
||||
"migrate": "yarn clean && yarn build && yarn typeorm migration:run -d dist/src/Bootstrap/DataSource.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.332.0",
|
||||
|
@ -38,6 +39,7 @@
|
|||
"@standardnotes/responses": "^1.13.9",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"@standardnotes/settings": "workspace:*",
|
||||
"@standardnotes/sncrypto-node": "workspace:*",
|
||||
"@standardnotes/time": "workspace:*",
|
||||
"axios": "^1.1.3",
|
||||
"cors": "2.8.5",
|
||||
|
@ -51,6 +53,7 @@
|
|||
"nodemon": "^2.0.19",
|
||||
"prettyjson": "^1.2.5",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"semver": "^7.5.1",
|
||||
"sqlite3": "^5.1.6",
|
||||
"typeorm": "^0.3.15",
|
||||
"ua-parser-js": "^1.0.32",
|
||||
|
@ -65,6 +68,7 @@
|
|||
"@types/jsonwebtoken": "^9.0.1",
|
||||
"@types/node": "^20.2.5",
|
||||
"@types/prettyjson": "^0.0.30",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultProps } from './SharedVaultProps'
|
||||
|
||||
export class SharedVault extends Entity<SharedVaultProps> {
|
||||
get id(): UniqueEntityId {
|
||||
return this._id
|
||||
}
|
||||
|
||||
private constructor(props: SharedVaultProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: SharedVaultProps, id?: UniqueEntityId): Result<SharedVault> {
|
||||
return Result.ok<SharedVault>(new SharedVault(props, id))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { Uuid, Timestamps } from '@standardnotes/domain-core'
|
||||
|
||||
export interface SharedVaultProps {
|
||||
userUuid: Uuid
|
||||
fileUploadBytesUsed: number
|
||||
fileUploadBytesLimit: number
|
||||
timestamps: Timestamps
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { SharedVault } from './SharedVault'
|
||||
|
||||
export interface SharedVaultsRepositoryInterface {
|
||||
findByUuid(uuid: string): Promise<SharedVault | null>
|
||||
save(sharedVault: SharedVault): Promise<void>
|
||||
remove(sharedVault: SharedVault): Promise<void>
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'shared_vaults' })
|
||||
export class TypeORMSharedVault {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('index_shared_vaults_on_user_uuid')
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'file_upload_bytes_used',
|
||||
type: 'int',
|
||||
})
|
||||
declare fileUploadBytesUsed: number
|
||||
|
||||
@Column({
|
||||
name: 'file_upload_bytes_limit',
|
||||
type: 'int',
|
||||
})
|
||||
declare fileUploadBytesLimit: number
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare createdAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare updatedAtTimestamp: number
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { Repository } from 'typeorm'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVaultsRepositoryInterface } from '../../Domain/SharedVault/SharedVaultsRepositoryInterface'
|
||||
import { TypeORMSharedVault } from './TypeORMSharedVault'
|
||||
import { SharedVault } from '../../Domain/SharedVault/SharedVault'
|
||||
|
||||
export class TypeORMSharedVaultRepository implements SharedVaultsRepositoryInterface {
|
||||
constructor(
|
||||
private ormRepository: Repository<TypeORMSharedVault>,
|
||||
private mapper: MapperInterface<SharedVault, TypeORMSharedVault>,
|
||||
) {}
|
||||
|
||||
async save(sharedVault: SharedVault): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sharedVault)
|
||||
|
||||
await this.ormRepository.save(persistence)
|
||||
}
|
||||
|
||||
async findByUuid(uuid: string): Promise<SharedVault | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault')
|
||||
.where('shared_vault.uuid = :uuid', {
|
||||
uuid,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.mapper.toDomain(persistence)
|
||||
}
|
||||
|
||||
async remove(sharedVault: SharedVault): Promise<void> {
|
||||
await this.ormRepository.remove(this.mapper.toProjection(sharedVault))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import { Timestamps, MapperInterface, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { SharedVault } from '../Domain/SharedVault/SharedVault'
|
||||
import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault'
|
||||
|
||||
export class SharedVaultPersistenceMapper implements MapperInterface<SharedVault, TypeORMSharedVault> {
|
||||
toDomain(projection: TypeORMSharedVault): SharedVault {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault from projection: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
|
||||
if (timestampsOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault from projection: ${timestampsOrError.getError()}`)
|
||||
}
|
||||
const timestamps = timestampsOrError.getValue()
|
||||
|
||||
const sharedVaultOrError = SharedVault.create(
|
||||
{
|
||||
userUuid,
|
||||
fileUploadBytesUsed: projection.fileUploadBytesUsed,
|
||||
fileUploadBytesLimit: projection.fileUploadBytesLimit,
|
||||
timestamps,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
if (sharedVaultOrError.isFailed()) {
|
||||
throw new Error(`Failed to create shared vault from projection: ${sharedVaultOrError.getError()}`)
|
||||
}
|
||||
const sharedVault = sharedVaultOrError.getValue()
|
||||
|
||||
return sharedVault
|
||||
}
|
||||
|
||||
toProjection(domain: SharedVault): TypeORMSharedVault {
|
||||
const typeorm = new TypeORMSharedVault()
|
||||
|
||||
typeorm.uuid = domain.id.toString()
|
||||
typeorm.userUuid = domain.props.userUuid.value
|
||||
typeorm.fileUploadBytesUsed = domain.props.fileUploadBytesUsed
|
||||
typeorm.fileUploadBytesLimit = domain.props.fileUploadBytesLimit
|
||||
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
|
||||
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
|
||||
|
||||
return typeorm
|
||||
}
|
||||
}
|
|
@ -4088,6 +4088,7 @@ __metadata:
|
|||
"@standardnotes/responses": "npm:^1.13.9"
|
||||
"@standardnotes/security": "workspace:*"
|
||||
"@standardnotes/settings": "workspace:*"
|
||||
"@standardnotes/sncrypto-node": "workspace:*"
|
||||
"@standardnotes/time": "workspace:*"
|
||||
"@types/cors": "npm:^2.8.9"
|
||||
"@types/dotenv": "npm:^8.2.0"
|
||||
|
@ -4097,6 +4098,7 @@ __metadata:
|
|||
"@types/newrelic": "npm:^9.13.0"
|
||||
"@types/node": "npm:^20.2.5"
|
||||
"@types/prettyjson": "npm:^0.0.30"
|
||||
"@types/semver": "npm:^7.5.0"
|
||||
"@types/ua-parser-js": "npm:^0.7.36"
|
||||
"@types/uuid": "npm:^8.3.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^5.59.2"
|
||||
|
@ -4119,6 +4121,7 @@ __metadata:
|
|||
prettier: "npm:^2.8.8"
|
||||
prettyjson: "npm:^1.2.5"
|
||||
reflect-metadata: "npm:0.1.13"
|
||||
semver: "npm:^7.5.1"
|
||||
sqlite3: "npm:^5.1.6"
|
||||
ts-jest: "npm:^29.1.0"
|
||||
typeorm: "npm:^0.3.15"
|
||||
|
@ -4656,7 +4659,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/semver@npm:^7.3.12":
|
||||
"@types/semver@npm:^7.3.12, @types/semver@npm:^7.5.0":
|
||||
version: 7.5.0
|
||||
resolution: "@types/semver@npm:7.5.0"
|
||||
checksum: dac255fae68157aec375fdb79d483a161c1b9c58e0ab9e18936dd1e9b89dd0ff85d64e482b1505de7e17455b404a0a530c4f9ddd6f21d333c2311c0068687b14
|
||||
|
|
Loading…
Reference in a new issue