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:
@@ -0,0 +1,16 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
yarn.lock
|
||||
|
||||
/dist
|
||||
|
||||
/api
|
||||
/services
|
||||
/models
|
||||
/subscribers
|
||||
/__mocks__
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user