feat(medusa-file-local): Local file storage plugin (#4118)

Simple local file storage to eliminate/reduce friction related to file services when developers are trying out Medusa for the first time
This commit is contained in:
Oliver Windall Juhl
2023-05-22 09:11:38 +00:00
committed by GitHub
parent 1709c0cc69
commit c4aae6b976
8 changed files with 578 additions and 7 deletions
+16
View File
@@ -0,0 +1,16 @@
/lib
node_modules
.DS_store
.env*
/*.js
!index.js
yarn.lock
/dist
/api
/services
/models
/subscribers
/__mocks__
+49
View File
@@ -0,0 +1,49 @@
# Local file storage
Store uploaded files to your Medusa backend locally.
> Not suited for production environments
[Plugin Documentation](https://docs.medusajs.com/plugins/file-service/local) | [Medusa Website](https://medusajs.com) | [Medusa Repository](https://github.com/medusajs/medusa)
## Features
- Store product images locally
---
## Prerequisites
- [Medusa backend](https://docs.medusajs.com/development/backend/install)
---
## How to Install
1\. Run the following command in the directory of the Medusa backend:
```bash
npm install medusa-file-local
```
2 \. In `medusa-config.js` add the following at the end of the `plugins` array:
```js
const plugins = [
// ...
`medusa-file-local`
]
```
---
## Test the Plugin
1\. Run the following command in the directory of the Medusa backend to run the backend:
```bash
npm run start
```
2\. Upload an image for a product using the admin dashboard or using [the Admin APIs](https://docs.medusajs.com/api/admin#tag/Upload).
+36
View File
@@ -0,0 +1,36 @@
{
"name": "medusa-file-local",
"version": "1.0.0",
"description": "Local file plugin",
"main": "dist/index.js",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/medusa-file-local"
},
"author": "Medusa",
"license": "MIT",
"devDependencies": {
"@medusajs/medusa": "1.10.1",
"cross-env": "^5.2.1",
"jest": "^25.5.4",
"typescript": "^4.9.5"
},
"scripts": {
"prepare": "cross-env NODE_ENV=production yarn run build",
"test": "jest --passWithNoTests src",
"build": "tsc",
"watch": "tsc --watch"
},
"peerDependencies": {
"medusa-interfaces": "1.3.7"
},
"gitHead": "81a7ff73d012fda722f6e9ef0bd9ba0232d37808",
"keywords": [
"medusa-plugin",
"medusa-plugin-file"
]
}
@@ -0,0 +1,11 @@
import express from "express"
export default (rootDirectory, pluginOptions) => {
const app = express.Router()
const uploadDir = pluginOptions.upload_dir ?? "uploads/images"
app.use(`/${uploadDir}`, express.static(uploadDir))
return app
}
@@ -0,0 +1,69 @@
import {
AbstractFileService,
FileServiceGetUploadStreamResult,
FileServiceUploadResult,
IFileService,
} from "@medusajs/medusa"
import fs from "fs"
import { parse } from "path"
class LocalService extends AbstractFileService implements IFileService {
protected uploadDir_: string
protected backendUrl_: string
constructor({}, options) {
super({}, options)
this.uploadDir_ = options.upload_dir || "uploads/images"
this.backendUrl_ = options.backend_url || "http://localhost:9000"
}
async upload(file: Express.Multer.File): Promise<FileServiceUploadResult> {
return await this.uploadFile(file)
}
async uploadProtected(file: Express.Multer.File) {
return await this.uploadFile(file, {})
}
async uploadFile(
file: Express.Multer.File,
options = {}
): Promise<{ url: string }> {
const parsedFilename = parse(file.originalname)
const fileKey = `${parsedFilename.name}-${Date.now()}${parsedFilename.ext}`
return new Promise((resolve, reject) => {
fs.copyFile(file.path, `${this.uploadDir_}/${fileKey}`, (err) => {
if (err) {
throw err
}
const fileUrl = `${this.backendUrl_}/${this.uploadDir_}/${fileKey}`
resolve({ url: fileUrl })
})
})
}
async delete(file): Promise<void> {
throw Error("Not implemented")
}
async getUploadStreamDescriptor(
fileData
): Promise<FileServiceGetUploadStreamResult> {
throw Error("Not implemented")
}
async getDownloadStream(fileData): Promise<NodeJS.ReadableStream> {
throw Error("Not implemented")
}
async getPresignedDownloadUrl(fileData): Promise<string> {
throw Error("Not implemented")
}
}
export default LocalService
+30
View File
@@ -0,0 +1,30 @@
{
"compilerOptions": {
"lib": ["es5", "es6", "es2019"],
"target": "es5",
"outDir": "./dist",
"rootDir": "src",
"esModuleInterop": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true // to use ES5 specific tooling
},
"include": ["src"],
"exclude": [
"dist",
"src/**/__tests__",
"src/**/__mocks__",
"src/**/__fixtures__",
"node_modules"
]
}