@drax/media-back 2.11.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/controllers/FileController.js +38 -0
  2. package/dist/controllers/MediaController.js +42 -1
  3. package/dist/factory/services/FileServiceFactory.js +30 -0
  4. package/dist/index.js +13 -2
  5. package/dist/interfaces/IFile.js +1 -0
  6. package/dist/interfaces/IFileRepository.js +1 -0
  7. package/dist/models/FileModel.js +49 -0
  8. package/dist/permissions/FilePermissions.js +13 -0
  9. package/dist/repository/mongo/FileMongoRepository.js +13 -0
  10. package/dist/repository/sqlite/FileSqliteRepository.js +37 -0
  11. package/dist/routes/FileRoutes.js +20 -0
  12. package/dist/schemas/FileSchema.js +51 -0
  13. package/dist/services/FileService.js +22 -0
  14. package/package.json +4 -4
  15. package/src/controllers/FileController.ts +50 -0
  16. package/src/controllers/MediaController.ts +48 -4
  17. package/src/factory/services/FileServiceFactory.ts +41 -0
  18. package/src/index.ts +30 -3
  19. package/src/interfaces/IFile.ts +67 -0
  20. package/src/interfaces/IFileRepository.ts +11 -0
  21. package/src/models/FileModel.ts +65 -0
  22. package/src/permissions/FilePermissions.ts +17 -0
  23. package/src/repository/mongo/FileMongoRepository.ts +22 -0
  24. package/src/repository/sqlite/FileSqliteRepository.ts +46 -0
  25. package/src/routes/FileRoutes.ts +36 -0
  26. package/src/schemas/FileSchema.ts +55 -0
  27. package/src/services/FileService.ts +36 -0
  28. package/test/modules/media/File/File-endpoints.test.ts +573 -0
  29. package/test/setup/MongoInMemory.ts +56 -0
  30. package/test/setup/TestSetup.ts +347 -0
  31. package/test/setup/data/admin-role.ts +13 -0
  32. package/test/setup/data/basic-user.ts +14 -0
  33. package/test/setup/data/file-role.ts +16 -0
  34. package/test/setup/data/one-tenant.ts +6 -0
  35. package/test/setup/data/restricted-role.ts +11 -0
  36. package/test/setup/data/root-user.ts +14 -0
  37. package/test/setup/data/tenant-one-user.ts +13 -0
  38. package/test/setup/data/tenant-two-user.ts +14 -0
  39. package/test/setup/data/two-tenant.ts +6 -0
  40. package/test/utils/StoreManager.test.ts +1 -1
  41. package/tsconfig.tsbuildinfo +1 -1
  42. package/types/controllers/FileController.d.ts +10 -0
  43. package/types/controllers/FileController.d.ts.map +1 -0
  44. package/types/controllers/MediaController.d.ts.map +1 -1
  45. package/types/factory/services/FileServiceFactory.d.ts +8 -0
  46. package/types/factory/services/FileServiceFactory.d.ts.map +1 -0
  47. package/types/index.d.ts +13 -1
  48. package/types/index.d.ts.map +1 -1
  49. package/types/interfaces/IFile.d.ts +63 -0
  50. package/types/interfaces/IFile.d.ts.map +1 -0
  51. package/types/interfaces/IFileRepository.d.ts +6 -0
  52. package/types/interfaces/IFileRepository.d.ts.map +1 -0
  53. package/types/models/FileModel.d.ts +15 -0
  54. package/types/models/FileModel.d.ts.map +1 -0
  55. package/types/permissions/FilePermissions.d.ts +13 -0
  56. package/types/permissions/FilePermissions.d.ts.map +1 -0
  57. package/types/repository/mongo/FileMongoRepository.d.ts +9 -0
  58. package/types/repository/mongo/FileMongoRepository.d.ts.map +1 -0
  59. package/types/repository/sqlite/FileSqliteRepository.d.ts +18 -0
  60. package/types/repository/sqlite/FileSqliteRepository.d.ts.map +1 -0
  61. package/types/routes/FileRoutes.d.ts +4 -0
  62. package/types/routes/FileRoutes.d.ts.map +1 -0
  63. package/types/schemas/FileSchema.d.ts +65 -0
  64. package/types/schemas/FileSchema.d.ts.map +1 -0
  65. package/types/services/FileService.d.ts +12 -0
  66. package/types/services/FileService.d.ts.map +1 -0
@@ -0,0 +1,67 @@
1
+ interface IFileBase {
2
+ filename: string
3
+ relativePath: string
4
+ absolutePath: string
5
+ url: string
6
+ description?: string
7
+ tags?: Array<string>
8
+ mimetype: string
9
+ encoding: string
10
+ extension: string
11
+ size: number
12
+ type: string
13
+ lastAccess: Date
14
+ createdBy?: {
15
+ id?: string
16
+ username?: string
17
+ }
18
+ updatedBy?: {
19
+ id?: string
20
+ username?: string
21
+ }
22
+ createdFor?: string
23
+ ttlSeconds: number
24
+ expiresAt?: Date
25
+ isPublic?: boolean
26
+ hits?: number
27
+ createdAt?: Date
28
+ updatedAt?: Date
29
+ tenant?: any
30
+ }
31
+
32
+ interface IFile {
33
+ _id: string
34
+ filename: string
35
+ relativePath: string
36
+ absolutePath: string
37
+ url: string
38
+ description?: string
39
+ tags?: Array<string>
40
+ mimetype: string
41
+ encoding: string
42
+ extension: string
43
+ size: number
44
+ type: string
45
+ lastAccess: Date
46
+ createdBy?: {
47
+ id?: string
48
+ username?: string
49
+ }
50
+ updatedBy?: {
51
+ id?: string
52
+ username?: string
53
+ }
54
+ createdFor?: string
55
+ ttlSeconds: number
56
+ expiresAt?: Date
57
+ isPublic?: boolean
58
+ hits?: number
59
+ createdAt?: Date
60
+ updatedAt?: Date
61
+ tenant?: any
62
+ }
63
+
64
+ export type {
65
+ IFileBase,
66
+ IFile
67
+ }
@@ -0,0 +1,11 @@
1
+
2
+ import type {IFile, IFileBase} from './IFile'
3
+ import {IDraxCrudRepository} from "@drax/crud-share";
4
+
5
+ interface IFileRepository extends IDraxCrudRepository<IFile, IFileBase, IFileBase>{
6
+
7
+ }
8
+
9
+ export {IFileRepository}
10
+
11
+
@@ -0,0 +1,65 @@
1
+ import {mongoose} from '@drax/common-back';
2
+ import {PaginateModel} from "mongoose";
3
+ import uniqueValidator from 'mongoose-unique-validator';
4
+ import mongoosePaginate from 'mongoose-paginate-v2'
5
+ import type {IFile} from '../interfaces/IFile'
6
+
7
+ const FileSchema = new mongoose.Schema<IFile>({
8
+ filename: {type: String, required: true, index: false, unique: false},
9
+ relativePath: {type: String, required: true, index: true, unique: false},
10
+ absolutePath: {type: String, required: true, index: false, unique: false},
11
+ url: {type: String, required: true, index: false, unique: false},
12
+ description: {type: String, required: false, index: false, unique: false},
13
+ tags: [{type: String, required: false, index: false, unique: false}],
14
+ mimetype: {type: String, required: true, index: false, unique: false},
15
+ encoding: {type: String, required: false, index: false, unique: false},
16
+ extension: {type: String, required: false, index: false, unique: false},
17
+ size: {type: Number, required: true, index: false, unique: false},
18
+ type: {type: String, required: false, index: false, unique: false},
19
+ lastAccess: {type: Date, required: false, index: false, unique: false},
20
+ createdBy: {
21
+ id: {type: mongoose.Schema.Types.ObjectId, required: false, index: false, unique: false},
22
+ username: {type: String, required: false, index: false, unique: false}
23
+ },
24
+ updatedBy: {
25
+ id: {type: mongoose.Schema.Types.ObjectId, required: false, index: false, unique: false},
26
+ username: {type: String, required: false, index: false, unique: false}
27
+ },
28
+ createdFor: {type: String, required: false, index: false, unique: false},
29
+ ttlSeconds: {type: Number, required: false , index: false, unique: false},
30
+ expiresAt: {type: Date, required: false, index: false, unique: false},
31
+ isPublic: {type: Boolean, required: false, index: false, unique: false},
32
+ hits: {type: Number, required: false, index: false, unique: false},
33
+ tenant: {type: mongoose.Schema.Types.ObjectId, ref: 'Tenant', required: false, index: false, unique: false},
34
+ }, {timestamps: true});
35
+
36
+ FileSchema.plugin(uniqueValidator, {message: 'validation.unique'});
37
+ FileSchema.plugin(mongoosePaginate);
38
+
39
+ FileSchema.virtual("id").get(function () {
40
+ return this._id.toString();
41
+ });
42
+
43
+
44
+ FileSchema.set('toJSON', {getters: true, virtuals: true});
45
+
46
+ FileSchema.set('toObject', {getters: true, virtuals: true});
47
+
48
+
49
+ FileSchema.pre("validate", function (next) {
50
+ if (this.ttlSeconds && !this.expiresAt) {
51
+ this.expiresAt = new Date(Date.now() + this.ttlSeconds * 1000)
52
+ }
53
+ next()
54
+ })
55
+
56
+ const MODEL_NAME = 'File';
57
+ const COLLECTION_NAME = 'File';
58
+ const FileModel = mongoose.model<IFile, PaginateModel<IFile>>(MODEL_NAME, FileSchema, COLLECTION_NAME);
59
+
60
+ export {
61
+ FileSchema,
62
+ FileModel
63
+ }
64
+
65
+ export default FileModel
@@ -0,0 +1,17 @@
1
+
2
+ enum FilePermissions {
3
+
4
+ Create = "file:create",
5
+ Update = "file:update",
6
+ Delete = "file:delete",
7
+ View = "file:view",
8
+ ViewAll = "file:viewAll",
9
+ UpdateAll = "file:updateAll",
10
+ DeleteAll = "file:deleteAll",
11
+ Manage = "file:manage"
12
+
13
+ }
14
+
15
+ export { FilePermissions };
16
+ export default FilePermissions;
17
+
@@ -0,0 +1,22 @@
1
+
2
+ import {AbstractMongoRepository} from "@drax/crud-back";
3
+ import {FileModel} from "../../models/FileModel.js";
4
+ import type {IFileRepository} from '../../interfaces/IFileRepository'
5
+ import type {IFile, IFileBase} from "../../interfaces/IFile";
6
+
7
+
8
+ class FileMongoRepository extends AbstractMongoRepository<IFile, IFileBase, IFileBase> implements IFileRepository {
9
+
10
+ constructor() {
11
+ super();
12
+ this._model = FileModel;
13
+ this._searchFields = ['filename', 'url', 'description', 'tags', 'mimetype', 'extension', 'type'];
14
+ this._populateFields = ['tenant'];
15
+ this._lean = true
16
+ }
17
+
18
+ }
19
+
20
+ export default FileMongoRepository
21
+ export {FileMongoRepository}
22
+
@@ -0,0 +1,46 @@
1
+
2
+ import {AbstractSqliteRepository} from "@drax/crud-back";
3
+ import type {IFileRepository} from '../../interfaces/IFileRepository'
4
+ import type {IFile, IFileBase} from "../../interfaces/IFile";
5
+ import {SqliteTableField} from "@drax/common-back";
6
+
7
+ class FileSqliteRepository extends AbstractSqliteRepository<IFile, IFileBase, IFileBase> implements IFileRepository {
8
+
9
+ protected db: any;
10
+ protected tableName: string = 'File';
11
+ protected dataBaseFile: string;
12
+ protected searchFields: string[] = ['filename', 'relativePath', 'absolutePath', 'url', 'description', 'tags', 'mimetype', 'extension', 'type'];
13
+ protected booleanFields: string[] = ['isPublic'];
14
+ protected identifier: string = '_id';
15
+ protected populateFields = [
16
+
17
+ ]
18
+ protected verbose: boolean = false;
19
+ protected tableFields: SqliteTableField[] = [
20
+ {name: "filename", type: "TEXT", unique: undefined, primary: false},
21
+ {name: "relativePath", type: "TEXT", unique: undefined, primary: false},
22
+ {name: "absolutePath", type: "TEXT", unique: undefined, primary: false},
23
+ {name: "url", type: "TEXT", unique: undefined, primary: false},
24
+ {name: "description", type: "TEXT", unique: undefined, primary: false},
25
+ {name: "tags", type: "TEXT", unique: undefined, primary: false},
26
+ {name: "mimetype", type: "TEXT", unique: undefined, primary: false},
27
+ {name: "encoding", type: "TEXT", unique: undefined, primary: false},
28
+ {name: "extension", type: "TEXT", unique: undefined, primary: false},
29
+ {name: "size", type: "REAL", unique: undefined, primary: false},
30
+ {name: "type", type: "TEXT", unique: undefined, primary: false},
31
+ {name: "lastAccess", type: "TEXT", unique: undefined, primary: false},
32
+ {name: "createdAt", type: "TEXT", unique: undefined, primary: false},
33
+ {name: "updatedAt", type: "TEXT", unique: undefined, primary: false},
34
+ {name: "createdBy", type: "TEXT", unique: undefined, primary: false},
35
+ {name: "updatedBy", type: "TEXT", unique: undefined, primary: false},
36
+ {name: "createdFor", type: "TEXT", unique: undefined, primary: false},
37
+ {name: "ttlSeconds", type: "REAL", unique: undefined, primary: false},
38
+ {name: "expiresAt", type: "TEXT", unique: undefined, primary: false},
39
+ {name: "isPublic", type: "TEXT", unique: undefined, primary: false},
40
+ {name: "hits", type: "REAL", unique: undefined, primary: false}
41
+ ]
42
+
43
+ }
44
+
45
+ export default FileSqliteRepository
46
+ export {FileSqliteRepository}
@@ -0,0 +1,36 @@
1
+
2
+ import FileController from "../controllers/FileController.js";
3
+ import {CrudSchemaBuilder} from "@drax/crud-back";
4
+ import {FileSchema, FileBaseSchema} from '../schemas/FileSchema.js'
5
+
6
+ async function FileRoutes(fastify, options) {
7
+
8
+ const controller: FileController = new FileController()
9
+ const schemas = new CrudSchemaBuilder(FileSchema, FileBaseSchema,FileBaseSchema, 'File', 'openapi-3.0', ['media']);
10
+
11
+ fastify.get('/api/file', {schema: schemas.paginateSchema}, (req,rep) => controller.paginate(req,rep))
12
+
13
+ fastify.get('/api/file/find', {schema: schemas.findSchema}, (req,rep) => controller.find(req,rep))
14
+
15
+ fastify.get('/api/file/search', {schema: schemas.searchSchema}, (req,rep) => controller.search(req,rep))
16
+
17
+ fastify.get('/api/file/:id', {schema: schemas.findByIdSchema}, (req,rep) => controller.findById(req,rep))
18
+
19
+ fastify.get('/api/file/find-one', {schema: schemas.findOneSchema}, (req,rep) => controller.findOne(req,rep))
20
+
21
+ fastify.get('/api/file/group-by', {schema: schemas.groupBySchema}, (req,rep) => controller.groupBy(req,rep))
22
+
23
+ fastify.post('/api/file', {schema: schemas.createSchema}, (req,rep) =>controller.create(req,rep))
24
+
25
+ fastify.put('/api/file/:id', {schema: schemas.updateSchema}, (req,rep) =>controller.update(req,rep))
26
+
27
+ fastify.patch('/api/file/:id', {schema: schemas.updateSchema}, (req,rep) =>controller.updatePartial(req,rep))
28
+
29
+ fastify.delete('/api/file/:id', {schema: schemas.deleteSchema}, (req,rep) =>controller.delete(req,rep))
30
+
31
+ fastify.get('/api/file/export', (req,rep) =>controller.export(req,rep))
32
+
33
+ }
34
+
35
+ export default FileRoutes;
36
+ export {FileRoutes}
@@ -0,0 +1,55 @@
1
+ import {z} from 'zod';
2
+
3
+
4
+ const FileBaseSchema = z.object({
5
+ filename: z.string().min(1, 'validation.required'),
6
+ relativePath: z.string().min(1, 'validation.required'),
7
+ absolutePath: z.string().min(1, 'validation.required'),
8
+ url: z.string().min(1, 'validation.required'),
9
+ description: z.string().default('').nullish(),
10
+ tags: z.array(z.string()).optional().default([]),
11
+ mimetype: z.string().nullish(),
12
+ encoding: z.string().nullish(),
13
+ extension: z.string().nullish(),
14
+ size: z.number().min(0, 'validation.required').nullish(),
15
+ type: z.string().nullish(),
16
+ lastAccess: z.coerce.date({error: "validation.date"}).nullish(),
17
+ createdFor: z.string().nullish(),
18
+ ttlSeconds: z.number().default(0).nullish(),
19
+ expiresAt: z.coerce.date().nullish(),
20
+ isPublic: z.boolean().nullish(),
21
+ hits: z.number().default(0),
22
+ createdBy: z.object({
23
+ id: z.string().nullish(),
24
+ username: z.string().nullish()
25
+ }).nullish(),
26
+ updatedBy: z.object({
27
+ id: z.string().nullish(),
28
+ username: z.string().nullish()
29
+ }).nullish(),
30
+ tenant: z.coerce.string().nullish(),
31
+ });
32
+
33
+ const FileSchema = FileBaseSchema
34
+ .omit({absolutePath: true})
35
+ .extend({
36
+ _id: z.coerce.string(),
37
+ createdAt: z.coerce.date(),
38
+ updatedAt: z.coerce.date(),
39
+ expiresAt: z.coerce.date().nullish(),
40
+ createdBy: z.object({
41
+ id: z.coerce.string().nullish(),
42
+ username: z.string().nullish()
43
+ }).nullish(),
44
+ updatedBy: z.object({
45
+ id: z.coerce.string().nullish(),
46
+ username: z.string().nullish()
47
+ }).nullish(),
48
+ tenant: z.object({
49
+ _id: z.coerce.string(),
50
+ name: z.string()
51
+ }).nullish()
52
+ })
53
+
54
+ export default FileSchema;
55
+ export {FileSchema, FileBaseSchema}
@@ -0,0 +1,36 @@
1
+
2
+ import type{IFileRepository} from "../interfaces/IFileRepository.js";
3
+ import type {IFileBase, IFile} from "../interfaces/IFile.js";
4
+ import {AbstractService} from "@drax/crud-back";
5
+ import type {ZodObject, ZodRawShape} from "zod";
6
+
7
+ class FileService extends AbstractService<IFile, IFileBase, IFileBase> {
8
+
9
+
10
+ constructor(FileRepository: IFileRepository, baseSchema?: ZodObject<ZodRawShape>, fullSchema?: ZodObject<ZodRawShape>) {
11
+ super(FileRepository, baseSchema, fullSchema);
12
+
13
+ this._validateOutput = true
14
+
15
+ }
16
+
17
+ async registerUploadedFile(data: IFileBase): Promise<IFile> {
18
+ return await this.create(data);
19
+ }
20
+
21
+ async registerDownloadHit(relativePath: string): Promise<IFile | null> {
22
+ const file = await this.findOneBy('relativePath', relativePath);
23
+ if (!file) {
24
+ return null;
25
+ }
26
+
27
+ return await this.updatePartial(file._id, {
28
+ hits: (file.hits || 0) + 1,
29
+ lastAccess: new Date()
30
+ });
31
+ }
32
+
33
+ }
34
+
35
+ export default FileService
36
+ export {FileService}