{"id":20482169,"url":"https://github.com/leaonline/grid-factory","last_synced_at":"2026-05-05T15:41:12.111Z","repository":{"id":41872578,"uuid":"264278362","full_name":"leaonline/grid-factory","owner":"leaonline","description":"Simple factory to create FilesCollections","archived":false,"fork":false,"pushed_at":"2025-01-13T21:34:59.000Z","size":495,"stargazers_count":1,"open_issues_count":6,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-16T04:14:18.502Z","etag":null,"topics":["factory","files","gridfs","gridfs-bucket","hacktoberfest","meteor","meteor-files","meteorjs","mongo"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leaonline.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-15T19:15:41.000Z","updated_at":"2023-09-25T09:40:19.000Z","dependencies_parsed_at":"2022-08-11T19:50:52.432Z","dependency_job_id":null,"html_url":"https://github.com/leaonline/grid-factory","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fgrid-factory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fgrid-factory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fgrid-factory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fgrid-factory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leaonline","download_url":"https://codeload.github.com/leaonline/grid-factory/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242058084,"owners_count":20065062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["factory","files","gridfs","gridfs-bucket","hacktoberfest","meteor","meteor-files","meteorjs","mongo"],"created_at":"2024-11-15T16:11:52.181Z","updated_at":"2026-05-05T15:41:12.083Z","avatar_url":"https://github.com/leaonline.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Meteor Grid-Factory\n\n[![Test suite](https://github.com/leaonline/grid-factory/actions/workflows/test-suite.yml/badge.svg)](https://github.com/leaonline/grid-factory/actions/workflows/test-suite.yml)\n[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)\n[![built with Meteor](https://img.shields.io/badge/Meteor%20package-1.1.0-green?logo=meteor\u0026logoColor=white)](https://meteor.com)\n[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)\n![GitHub](https://img.shields.io/github/license/leaonline/publication-factory)\n\nCreate **FilesCollections** with integrated **GridFS** storage.\nLightweight. Simple.\n\nWith this package you can easily create multiple **`ostrio:files`** collections\n(*FilesCollections*) that work with [MongoDB's\nGridFS](https://docs.mongodb.com/manual/core/gridfs/) system **out-of-the-box**.\n\n## Background / reasons\n\nIt can be a real hassle to introduce gridFS as storage to your project.\nThis package aims to abstract common logic into an easy and accessible API while\nensuring to let you override anything in case you need a fine-tuned custom\nbehavior.\n\nThe abtract factory allows you to create configurations on a higher level that\napply to all your FilesCollections, while you still can fine-tune on the\ncollection level. Supports all constructor arguments of FilesCollection.\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Why such a specialized package?](#why-such-a-specialized-package)\n  - [What is covered / what is not (yet)](#what-is-covered--what-is-not-yet)\n    - [Creation](#creation)\n    - [`onBeforeUpload`](#onbeforeupload)\n    - [`onAfterUpload`](#onafterupload)\n    - [`protected`](#protected)\n    - [`interceptDownload`](#interceptdownload)\n    - [`onBeforeRemove`](#onbeforeremove)\n    - [`afterRemove`](#afterremove)\n- [Getting started](#getting-started)validateUser\n  - [1. Install this package via](#1-install-this-package-via)\n  - [2. Optionally install packages for mime-check and transformations](#2-optionally-install-packages-for-mime-check-and-transformations)\n  - [3. Import the abstract factory](#3-import-the-abstract-factory)\n  - [4. Create a server side FilesCollection factory](#4-create-a-server-side-filescollection-factory)\n    - [Minimal example](#minimal-example)\n  - [5. Create a client side factory](#5-create-a-client-side-factory)\n- [Changelog](#changelog)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Why such a specialized package?\n\nThis package is designed for projects, that can't rely on third-party storages,\nbecause one or more of the following applies:\n\n- there are concerns about privacy or security when using a third party storage\n- the application has to be shipped in a \"all-in-one\" monolith\n- the app is intended for intranet use and connection to the \"outside\" is\n  prohibited\n- whatever you can think of....\n\nThe use case may be not very common (Meteor + `ostrio:files` + GridFS) but if\nit's for you, this package makes file handling much easier and consistent.\n\n### What is covered by this package\n\nThis package has some **out-of-the-box** functionality that covers the following\npoints.\n\n#### Creation\n\n- [ x ] Creating new `FilesCollection` instances\n- [ x ] Supporting full `FilesCollection` constructor\n- [ x ] Allows to override package internals by explicitly passing the hooks\n       (`onAfterUpload` etc.)\n- [ x ] Code-splitting (server/client)\n- [ x ] Using GridFS buckets instead of deprecated `gridfs-stream`\n- [ x ] Adapter for translation\n- [ x ] Creating new `FilesCollection` instances\n\n#### `onBeforeUpload`\n\nThis package has some some default behavior defined for the `onBeforeUpload`\nhook. You can override it completely or hook into it's behavior using the\nfollowing parameters:\n\n- [ x ] check file size via `maxSize` (Number)\n- [ x ] check file extensions via `extensions` ([String])\n- [ x ] check permissions via `validateUser` (Function)\n- [ x ] check permissions via `validateUser` (Function)\n\n#### `onAfterUpload`\n\nThe default behavior for `onAfterUpload` is to check the mime of the uploaded\nfile and move it to the Grid. However, you can hook into this process, too:\n\n- [ x ] validate user via `validateUser` (Function)\n- [ x ] validate mime via `validateMime` (Function)\n- [ x ] transform additional versions (e.g. thumbnails, converted videos, etc.)\n        via `transformVersions` (Function)\n\n#### `protected`\n\n- [ x ] validate user via `validateUser` (Function)\n\n#### `interceptDownload`\n\n- [ x ] falls back to find a valid version, if request to a non-existent version\n        fails\n- [ x ] streams the file from the GridFS bucket\n- [ x ] handles errors with an error response\n- [ x ] sets the correct content disposition, depending on `download` query attribute\n- [ x ] 206 response streaming\n\n#### `onBeforeRemove`\n\n- [ x ] validate user via `validateUser` (Function)\n\n#### `afterRemove`\n\n- [ x ] removes file, including all versions, from the GridFS and the FilesCollection\n\n## Getting started\n\n### 1. Install this package via\n\n```bash\nmeteor add leaonline:files-collection-factory ostrio:files\n```\n\nWe decoupled this package from `ostrio:files` so your host project can manage\nthe versioning.\n\n### 2. Optionally install packages for mime-check and transformations\n\nIf you want to check the mime you can use packages like `mmmagic` and `mime-types` to check for the correct mime type.\nOf course you can implement your mime-check a total different way, too.\n\n```bash\nmeteor npm install --save mmmagic mime-types\n```\n\nIf you want to transform your images or videos you also need the respective packages for that.\nOften you will also have to install additional software / packages on your host OS, since the npm packages\n(e.g. for image magick / graphics magic) are usually just wrappers for the OS-level packages.\n\n### 3. Import the abstract factory\n\nThe package exports different APIs for client and server but you import it the same way on server and client:\n\n```javascript\nimport { createGridFilesFactory } from 'meteor/leaonline:grid-factory'\n```\n\nFrom here you have to consider the [FilesCollection architecture](https://github.com/VeliovGroup/Meteor-Files/blob/master/docs/constructor.md)\nin order to manage access, post processing, removal etc.\n\n### 4. Create a server side FilesCollection factory\n\nOn the server side you can use the following abstract factory api:\n\n```javascript\n({\n  i18nFactory: Function, // translator function, str =\u003e translatedStr\n  fs: Object, // the node file-system module\n  bucketFactory: Function, // a function that returns a gridFS bucket\n  defaultBucket: String, // a default name for the bucket to be used\n  createObjectId: Function, // a function that creates an Object Id by a given GridFS id\n  onError: Function, // logs errors for all collections across all factories\n  debug: Boolean,\n    ...config // all valid config, that can be passed to the FilesCollection server constructor\n}) =\u003e Function =\u003e FilesCollection\n```\n\nThe factory Function that is returned contains the following api:\n\n```javascript\n({\n  bucketName: String, // override the defaultBucket, if desired\n  maxSize: Number, // number in bytes to limit the maximum size for files of this collection\n  extensions: [String], // a list of supported extensions\n  validateUser: Function, // a Function that checks permission of the current user/file and returns falsy/truthy\n  validateMime: Function, // async Function that checks permission of the current file/mime and returns falsy/truthy\n  transformVersions: Function, // async Function that transforms the file to different versions\n  onError: Function // logs errors, overrides onError from abstract factory\n}) =\u003e FilesCollection\n```\n\n#### Minimal example\n\nThe following example shows a minimal abstract GridFactory:\n\n```javascript\nimport { MongoInternals } from 'meteor/mongo'\nimport { createGridFilesFactory } from 'meteor/leaonline:grid-factory'\nimport { i18n } from '/path/to/i8n'\nimport fs from 'fs'\n\nconst debug = Meteor.isDevelopment\nconst i18nFactory = (...args) =\u003e i18n.get(...args)\nconst createObjectId = ({ gridFsFileId }) =\u003e new MongoInternals.NpmModule.ObjectID(gridFsFileId)\nconst bucketFactory = bucketName =\u003e \n  new MongoInternals.NpmModule.GridFSBucket(MongoInternals.defaultRemoteCollectionDriver().mongo.db, { bucketName })\nconst defaultBucket = 'fs' // resolves to fs.files / fs.chunks as default\nconst onError = error =\u003e console.error(error)\n\nconst createFilesCollection = createGridFilesFactory({ \n  i18nFactory, \n  fs, \n  bucketFactory, \n  defaultBucket, \n  createObjectId, \n  onError, \n  debug \n})\n\nconst ProfileImages = createFilesCollection({\n  collectionName: 'profileImages',\n  bucketName: 'images', // put image collections in the 'images' bucket\n  maxSize: 3072000, // 3 MB max\n  validateUser: function (userId, file, type, translate) {\n    // is this a valid and registered user?\n    if (!userId || Meteor.users.find(userId).count() !== 1) {\n      return false\n    }\n    \n    const isOwner = userId === file.userId\n    const isAdmin = ...\n    const isAllowedToDownload =  ...\n    \n    if (type === 'upload') {\n      return Roles.userIsInRole(userId, 'can-upload', 'mydomain.com') // example of using roles\n    }\n    \n    if (type === 'download') {\n      return isOwner || isAdmin || isAllowedToDownload // custom flags\n    }\n    \n    if (type === 'remove') {\n     // allow only owner to remove the file\n     return isOwner || isAdmin\n    }\n    \n    throw new Error(translate('unexpectedCodeReach'))\n  }\n})\n```\n\n### 5. Create a client side factory\n\nOn the server side you have less options to pass to the API:\n\n```javascript\n({\n  i18nFactory: Function, // translator function, str =\u003e translatedStr\n  debug: Boolean,\n  ...config // all valid config, that can be passed to the FilesCollection client constructor\n}) =\u003e Function =\u003e FilesCollection\n```\n\nThe factory Function that is returned contains the following api:\n\n```javascript\n({\n  bucketName: String, // override the defaultBucket, if desired\n  maxSize: Number, // number in bytes to limit the maximum size for files of this collection\n  extensions: [String], // a list of supported extensions\n  validateUser: Function, // a Function that checks permission of the current user/file and returns falsy/truthy\n  validateMime: Function, // async Function that checks permission of the current file/mime and returns falsy/truthy\n  transformVersions: Function, // async Function that transforms the file to different versions\n  onError: Function // logs errors, overrides onError from abstract factory\n}) =\u003e FilesCollection\n```\n\n## Contribution\n\nContributions are very welcomed! Please leave an issue before creating a PR in\norder to discuss feasibility and boundaries of a potential PR.\n\n## Testing\n\nWe provide a special Meteor project for tests for you. I contains scripts for\nlinting and testing out of the box:\n\n```bash\ncd test-proxy\nmeteor npm run setup\nmeteor npm run lint:code\nmeteor npm run lint:markdown\nmeteor npm run test:watch\n```\n\n## Changelog\n\n- **1.3.0**\n  - partial response (206) implemented\n  - onAfterUpload is now fully async with correct order; return value will not\n    be returned until all hooks are called (validateMime, transformVersions,\n    moveToGrid) and completed;\n  - file has a `processingComplete` flag that will indicate when the\n    `onAfterUpload` pipeline has been completed, which allows for example to\n    not link the file on the client until fully processed; make sure your schema\n    supports it!\n  - tests updated a lot\n\n- **1.2.0**\n  - security patch to prevent server process crashing if mime check fails\n  \n- **1.1.0**\n  - major bump for ostrio:files to support 1.x AND 2.x\n  - logging improved\n  - improved checks and loggins for all library functions\n  - tests added\n  - documentation improved\n  \n- **1.0.2**\n  - getGridFsFileId fix bug searching fallback versions\n\n- **1.0.1**\n  - allow skipping user validation for prototyping but raise a server warning\n  - README fix export name\n  - standardjs lint fix\n  \n## License\n\nMIT, see [LICENSE file](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleaonline%2Fgrid-factory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleaonline%2Fgrid-factory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleaonline%2Fgrid-factory/lists"}