/// import MongoDB from 'mongodb'; import { Stream, Readable } from 'stream'; import AWS from 'aws-sdk'; import { ContentId, IContentMetadata, IContentStorage, IFileStats, IUser, Permission, ILibraryName } from '../../types'; /** * This storage implementation stores content data in a MongoDB collection * and a S3 bucket. * The parameters and metadata of a H5P content object are stored in MongoDB, * while all files are put into S3 storage. */ export default class MongoS3ContentStorage implements IContentStorage { private s3; private mongodb; private options; /** * @param s3 the S3 content storage; Must be either set to a bucket or the * bucket must be specified in the options! * @param mongodb a MongoDB collection (read- and writable) * @param options options */ constructor(s3: AWS.S3, mongodb: MongoDB.Collection, options: { /** * If set, the function is called to retrieve a list of permissions * a user has on a certain content object. * This function can function as an adapter to your rights and * privileges system. */ getPermissions?: (contentId: ContentId, user: IUser) => Promise; /** * These characters will be removed from files that are saved to S3. * There is a very strict default list that basically only leaves * alphanumeric filenames intact. Should you need more relaxed * settings you can specify them here. */ invalidCharactersRegexp?: RegExp; /** * Indicates how long keys in S3 can be. Defaults to 1024. (S3 * supports 1024 characters, other systems such as Minio might only * support 255 on Windows). */ maxKeyLength?: number; /** * The ACL to use for uploaded content files. Defaults to private. */ s3Acl?: string; /** * The bucket to upload to and download from. (required) */ s3Bucket: string; }); /** * Indicates how long keys can be. */ private maxKeyLength; /** * Generates the S3 key for a file in a content object * @param contentId * @param filename * @returns the S3 key */ private static getS3Key; /** * Creates or updates a content object in the repository. Throws an error if * something went wrong. * @param metadata The metadata of the content (= h5p.json) * @param content the content object (= content/content.json) * @param user The user who owns this object. * @param contentId (optional) The content id to use * @returns The newly assigned content id */ addContent(metadata: IContentMetadata, content: any, user: IUser, contentId?: ContentId): Promise; /** * Adds a content file to an existing content object. Throws an error if * something went wrong. * @param contentId The id of the content to add the file to * @param filename The filename * @param stream A readable stream that contains the data * @param user The user who owns this object * @returns */ addFile(contentId: ContentId, filename: string, stream: Stream, user: IUser): Promise; /** * Checks if a piece of content exists in storage. * @param contentId the content id to check * @returns true if the piece of content exists */ contentExists(contentId: ContentId): Promise; /** * Deletes a content object and all its dependent files from the repository. * Throws errors if something goes wrong. * @param contentId The content id to delete. * @param user The user who wants to delete the content * @returns */ deleteContent(contentId: ContentId, user?: IUser): Promise; /** * Deletes a file from a content object. * @param contentId the content object the file is attached to * @param filename the file to delete */ deleteFile(contentId: ContentId, filename: string, user?: IUser): Promise; /** * Checks if a file exists. * @param contentId The id of the content to add the file to * @param filename the filename of the file to get * @returns true if the file exists */ fileExists(contentId: ContentId, filename: string): Promise; /** * Returns information about a content file (e.g. image or video) inside a * piece of content. * @param id the id of the content object that the file is attached to * @param filename the filename of the file to get information about * @param user the user who wants to retrieve the content file * @returns */ getFileStats(contentId: string, filename: string, user: IUser): Promise; /** * Returns a readable stream of a content file (e.g. image or video) inside a piece of content * Note: Make sure to handle the 'error' event of the Readable! This method * does not check if the file exists in storage to avoid the extra request. * However, this means that there will be an error when piping the Readable * to the response if the file doesn't exist! * @param contentId the id of the content object that the file is attached to * @param filename the filename of the file to get * @param user the user who wants to retrieve the content file * @returns */ getFileStream(contentId: ContentId, filename: string, user: IUser, rangeStart?: number, rangeEnd?: number): Promise; getMetadata(contentId: string, user?: IUser): Promise; getParameters(contentId: string, user?: IUser): Promise; /** * Calculates how often a library is in use. * @param library the library for which to calculate usage. * @returns asDependency: how often the library is used as subcontent in * content; asMainLibrary: how often the library is used as a main library */ getUsage(library: ILibraryName): Promise<{ asDependency: number; asMainLibrary: number; }>; /** * Returns an array of permissions that the user has on the piece of content * @param contentId the content id to check * @param user the user who wants to access the piece of content * @returns the permissions the user has for this content (e.g. download it, delete it etc.) */ getUserPermissions(contentId: ContentId, user: IUser): Promise; listContent(user?: IUser): Promise; /** * Gets the filenames of files added to the content with addContentFile(...) (e.g. images, videos or other files) * @param contentId the piece of content * @param user the user who wants to access the piece of content * @returns a list of files that are used in the piece of content, e.g. ['image1.png', 'video2.mp4'] */ listFiles(contentId: ContentId, user: IUser): Promise; /** * Removes invalid characters from filenames and enforces other filename * rules required by the storage implementation (e.g. filename length * restrictions). * @param filename the filename to sanitize; this can be a relative path * (e.g. "images/image1.png") * @returns the clean filename */ sanitizeFilename(filename: string): string; }