본문 바로가기

Node

[#. Node] 관리자 패널, 대시보드를 제공하는 Admin Dashboard AdminBro upload image 이미지 업로드 추가하기 4

반응형

 

 

 

 

 

 

 

admin-bro 대시보드에서 이미지를 업로드하고 싶다 

세팅해 보자

 

 

 

 

 

① [프로젝트명]/package.json

 

일단 내가 설치해서 사용하는 패키지들은 이렇다

 

...
"dependencies": {
  "admin-bro": "^2.2.4",
  "admin-bro-expressjs": "^2.0.2",
  "admin-bro-mongoose": "^0.5.0",
  "argon2": "^0.27.2",
  "bcrypt": "^5.0.1",
  "body-parser": "^1.19.0",
  "cookie-parser": "^1.4.5",
  "cors": "^2.8.5",
  "express": "^4.17.1",
  "express-formidable": "^1.2.0",
  "express-session": "^1.17.1",
  "jsonwebtoken": "^8.5.1",
  "moment": "^2.29.1",
  "mongoose": "^5.9.7",
  "save": "^2.4.0"
},
...

 

pacakage.json 파일이 있는 경로에서 명렁어를 입력해서 패키지들을 설치하자 

 

npm install

 

 

 

 

 

② [프로젝트명]/index.js

 

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
const config = require("./config/key");
const cookieParser = require("cookie-parser");
const AdminBro = require('admin-bro');
const options = require('./admin.options');
const buildAdminRouter = require('./admin.router');

const app = express();

mongoose
  .connect('[mongoDB URI 입력]', { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log("DB connected"))
  .catch(err => console.error(err));

const admin = new AdminBro(options);
const router = buildAdminRouter(admin);

app.use(admin.options.rootPath, router);

app.use('/uploads', express.static('uploads'));

// parse requests of content-type: application/json
app.use(bodyParser.json());
app.use(cors());

// parse requests of content-type: application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());

// set port, listen for requests
app.listen(5000, () => {
  console.log("Server is running on port 5000.");
});

 

 

 

 

 

③ [프로젝트명]/admin.options.js

 

const { default: AdminBro } = require('admin-bro');
const AdminBroMongoose = require('admin-bro-mongoose');

AdminBro.registerAdapter(AdminBroMongoose);

const AdminCompany = require('./models/company.admin');

/** @type {import('admin-bro').AdminBroOptions} */
const options = {
  resources: [AdminCompany],
};

module.exports = options;

 

 

 

 

 

④ [프로젝트명]/admin.router.js

 

const AdminBro = require('admin-bro');
const { buildAuthenticatedRouter } = require('admin-bro-expressjs');
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const config = require("./config/key");

const { Company } = require('./models/company.entity');

/**
 * @param {AdminBro} admin
 * @return {express.Router} router
 */

 const ADMIN = {
   email: 'admin@a.aa',	// 관리자 계정 
   password: '12341234',	// 관리자 비밀번호
   saveUninitialized: true
 };

const buildAdminRouter = (admin) => {
  const router = buildAuthenticatedRouter(admin, {
    cookieName: 'admin-bro',
    cookiePassword: 'testtest',
    authenticate: async (email, password) => {
      if (ADMIN.password === password && ADMIN.email === email) {
        return ADMIN
      }
        return null
      },
  }, null, {
    resave: false,
    saveUninitialized: true,
  });
  return router;
};

module.exports = buildAdminRouter;

 

 

 

 

 

⑤ [프로젝트명]/models/company.entity.js

 

const mongoose = require('mongoose');

const CompanySchema = new mongoose.Schema({
  companyName: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
  },
  address: {
    type: String,
  },
  profilePhotoLocation: {
    type: String,
  },
  encryptedPassword: {
    type: String,
    required: true,
  },
});

const Company = mongoose.model('Company', CompanySchema);

module.exports = { CompanySchema, Company };

 

 

 

 

 

⑥ [프로젝트명]/models/company.admin.js

 

const AdminBro = require('admin-bro');
const { Company } = require('./company.entity');

const {
  after: passwordAfterHook,
  before: passwordBeforeHook,
} = require('../actions/password.hook');

const {
  after: uploadAfterHook,
  before: uploadBeforeHook,
} = require('../actions/upload-image.hook');



/** @type {AdminBro.ResourceOptions} */
const options = {
  properties: {
    encryptedPassword: {
      isVisible: false,
    },
    profilePhotoLocation: {
      isVisible: false,
    },
    password: {
      type: 'password',
    },
    uploadImage: {
      components: {
        edit: AdminBro.bundle('../components/upload-image.edit.tsx'),
        list: AdminBro.bundle('../components/upload-image.list.tsx'),
      },
    },
  },
  actions: {
  new: {
    after: async (response, request, context) => {
      const modifiedResponse = await passwordAfterHook(response, request, context);
      return uploadAfterHook(modifiedResponse, request, context);
    },
    before: async (request, context) => {
      const modifiedRequest = await passwordBeforeHook(request, context);
      return uploadBeforeHook(modifiedRequest, context);
    },
  },
  edit: {
    after: async (response, request, context) => {
      const modifiedResponse = await passwordAfterHook(response, request, context);
      return uploadAfterHook(modifiedResponse, request, context);
    },
    before: async (request, context) => {
      const modifiedRequest = await passwordBeforeHook(request, context);
      return uploadBeforeHook(modifiedRequest, context);
    },
  },
  show: {
    isVisible: false,
  },
},
};

module.exports = {
  options,
  resource: Company,
};

 

 

 

 

 

 

⑦ [프로젝트명]/actions/password.hook.js

 

const argon2 = require('argon2');
const AdminBro = require('admin-bro');


/** @type {AdminBro.After<AdminBro.ActionResponse>} */
const after = async (response) => {
  if (response.record && response.record.errors && response.record.errors.encryptedPassword) {
    response.record.errors.password = response.record.errors.encryptedPassword;
  }
  return response;
};

/** @type {AdminBro.Before} */
const before = async (request) => {
  if (request.method === 'post') {
    const { password, ...otherParams } = request.payload;

    if (password) {
      const encryptedPassword = await argon2.hash(password);

      return {
        ...request,
        payload: {
          ...otherParams,
          encryptedPassword,
        },
      };
    }
  }
  return request;
};

module.exports = { after, before };

 

 

 

 

 

⑧ [프로젝트명]/actions/upload-image.hook.js

 

const path = require('path');
const fs = require('fs');
const AdminBro = require('admin-bro');


/** @type {AdminBro.After<AdminBro.ActionResponse>} */
const after = async (response, request, context) => {
  const { record, uploadImage } = context;

  if (record.isValid() && uploadImage) {
    const filePath = path.join('uploads', record.id().toString(), uploadImage.name);
    await fs.promises.mkdir(path.dirname(filePath), { recursive: true });

    await fs.promises.rename(uploadImage.path, filePath);

    await record.update({ profilePhotoLocation: `/${filePath}` });
  }
  return response;
};

/** @type {AdminBro.Before} */
const before = async (request, context) => {
  if (request.method === 'post') {
    const { uploadImage, ...otherParams } = request.payload;

    // eslint-disable-next-line no-param-reassign
    context.uploadImage = uploadImage;

    return {
      ...request,
      payload: otherParams,
    };
  }
  return request;
};

module.exports = { after, before };

 

 

 

 

 

⑨ [프로젝트명]/components/upload-image.edit.tsx

 

import React from 'react'
import { Label, Box, DropZone, BasePropertyProps, DropZoneProps, DropZoneItem } from 'admin-bro'

const Edit: React.FC<BasePropertyProps> = (props) => {
  const { property, onChange, record } = props

  const handleDropZoneChange: DropZoneProps['onChange'] = (files) => {
    onChange(property.name, files[0])
  }

  const uploadedPhoto = record.params.profilePhotoLocation
  const photoToUpload = record.params[property.name]

  return (
    <Box marginBottom="xxl">
      <Label>{property.label}</Label>
      <DropZone onChange={handleDropZoneChange}/>
      {uploadedPhoto && !photoToUpload && (
        <DropZoneItem src={uploadedPhoto} />
      )}
    </Box>
  )
}

export default Edit

 

 

 

 

 

⑩ [프로젝트명]/components/upload-image.list.tsx

 

import React from 'react'
import { Box, BasePropertyProps } from 'admin-bro'

const Edit: React.FC<BasePropertyProps> = (props) => {
  const { record } = props

  const srcImg = record.params['profilePhotoLocation']
  return (
    <Box>
      {srcImg ? (
        <img src={srcImg} width="100px"/>
      ) : 'no image'}
    </Box>
  )
}

export default Edit

 

 

 

 

 

 

node index.js

 

서버를 실행하고 localhost:5000/admin에 접속하면 대시보드가 잘 뜨는 것을 확인할 수 있다

 

 

 

 

 

 

 

이미지 drag and drop 영역도 잘 뜬다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

아래 유튜브 영상을 참고했다

www.youtube.com/watch?v=7WtKcFqJHho

 

반응형