npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hetamvc

v0.3.38

Published

Backend mvc framework for node expressJs

Downloads

28

Readme

HetaMvc

HetaMvc is MVC back-end framework that consist of expressJs in NodeJs based

Install it with the command below

    $ npm i -save hetamvc

아래 문구를 app.js등의 시작 js에 추가해서 실행해 줍니다.

   require('hetamvc').init({
        scanPath:app_route,	        //scan path  (required)
        route:express,		            //express route (required)
        //socket:socketIo,	                            //socket object
        //sequelize:require('./sequelize.config.js'),    //DB
        //mongodb:require('./mongodb.config.js'),       //Mongo
        //email:require('./email.config.js'),       //Email
        //locale:require('./locale.config.js'),       //Locale
    	//constants:require('./app.config.js'),   //Global Value 
    	//logger:require('./logger.config.js'),   //logging
	    //uploadPath:appConfig._upload.path             //File Upload path
        //forceAwait: true                          //force await mode 
        //rootPath: __dirname                       //강제 경로   app.js를 외부에서 호출시 import한 값의 경로가 바뀔수 있음 
    });

scanPath : route path 파일의 최상위 경로 (필수)
route : express object const express = express(); (필수)
socket : websocket object const socketIo = require('socket.io')(serv,{});
forceAwait : true일때 모든 file에 비동기 필요하면 자동으로 async await를 붙인다.
sequelize : sequelize object require('./sequelize.config.js')

    //sequelize.config.js (mysql)
    module.exports = {
        dialect: "mysql",
        username: "root",
        password: "xxxxxxx",
        passKey: "Encrypt_Key",    //Password encryption key
        database: "dbname",
        host: "localhost",
        port: 3306,
        logging: true,
        force: false,
        operatorsAliases: false,
        timezone: "-08:00",
        dialectOptions: {
            charset: 'utf8mb4',
            dateStrings: true,
            typeCast: true
        },
        define: {
            timestamps: false
        },
        pool:{
            max:20,
            min:5,
            idle:10000
        }
    };
    
    //sequelize.config.js (sqlite)
    module.exports = {
        dialect: "sqlite",
        force: false,       //강제 테이블변경
        operatorsAliases: false,    
        dialectOptions: {
            charset: 'utf8mb4',
            dateStrings: true,
            typeCast: true
        },
        define: {
            freezeTableName: true,  //테이블에s 제거
            timestamps: false    //false : 자동으로 CreatedAt, UpdatedAt 방지 
        },
        pool:{
            max:20,
            min:5,
            idle:10000
        },
        storage: __dirname+"/database.sqlite" // default Sequelize('sqlite::memory:');
    };

mongodb : mongodb 연결시 설정해줌 require('./mongodb.config.js')

    //mongodb.config.js
    module.exports = {
        username: "",
        password: "",
        passKey: "Encrypt_Key",    //Password encryption key
        database: "test",
        host: "localhost",
        port: 27017,
        dialect: "mongodb",
        options: {
            useNewUrlParser: true,
            useUnifiedTopology: true,	//pool
            serverSelectionTimeoutMS: 10000,
        }
        /* mongodb transaction을 사용할때 활성화
        ,replicaSet: {
            name : "bgarage_replica",
            hosts: [ // Primary를 제외한 나머지 기술
            {
                host: "bgaragemongo_slave",
                port: 27118
            },
            {
                host: "bgaragemongo_arbiter",
                port: 27119
            }
            ]
        }
        */
    };

constants : constants object 초기 상수값 require('./app.config.js')

    //app.config.js 
    const path = require('path');
    module.exports = {
        _email:{
            serverUrl: "http://{IP}"
        },
        _ip:'',
        _root: __dirname,
        _upload:{
            path: path.resolve(__dirname, './upload')
        }
    };

email : email사용하기 위해 smtp설정 require('./email.config.js')

    //email.config.js 
    module.exports = {
        smtp: "smtp.naver.com",
        port: 587,
        secure: false,
        user: "xxxxx",
        pass: "04ec1b40be3a75a12ae522cc38affab8", //Passwords created using @Encrypt can be created in the documentation referenced.
        passKey: "Encrypt_Key",    //Password encryption key
        from: "[email protected]"         //Pin the sending user
    };

logger : winston logger사용하기 위해 설정 require('./logger.config.js')

    //logger.config.js 
    module.exports = {
        level: "debug", //error:0, warn:1, info:2, verbose:3, debug:4, silly:5
        output:['console','file'], 	//default 'console'
        dir: 'logs'	//file directory
    };

locale : 다국어 사용을 위해 i18n설정 require('./locale.config.js')

    //locale.config.js 
    module.exports = {
        locales:['en', 'ko'],   //사용언어 설정 / 'de' 나 'ja' , 'fr' 등등 추가 가능 
        directory: '/locales', // 사용언어에 대한 템플릿폴더 생성위치,  
        defaultLocale: 'en',    //기본 사용언어 설정 
        cookie: 'lang',         //쿠키의 이름 설정, 개발자가 자유롭게 이름 설정가능
        autoReload: true,
        //updateFiles: false,   //없는 param을 있으면 json파일을 변경한다
        //syncFiles: false,		//로케일 정보 동기화 
        objectNotation:true 
    };

    //en.json
    {
        "home": {
		    "title": "test {{aa}}"
	    }
    }

File 구성


파일은 Component, Controller, Service, Model 이렇게 4가지 형태의 @Annotation으로 구분해주며
모든 파일은 Common @Annotation을 호출 할 수 있습니다.

  1. Common @Annotation
    변수로만 할당하며 모두 contructor안에서 정의한다.
    호출하는 객체는 모두 async await 처리를 자동으로 해준다.

    
    /** @NoAutoAwait () */ 
    class Test{
        constructor(){
    	    /** @Inject ('SettingMasterService') */
    	    this.SettingMasterService;
    
            /** @Websocket ('/ns') */ 
            this.websocket;
    
            /** @Sequelize () */ 
            this.sequelize;
    
            /** @Request () */ 
            this.request;
    
            /** @Render () */ 
            this.render;
    
            /** @Route () */ 
            this.route;
    
            /** @Constants () */ 
            this.constants;
    
            /** @Encrypt ('Encrypt_Key')    */ 
            this.encrypt;
    
            /** @Email () */ 
            this.email;
    
            /** @Locale () */ 
            this.i18n;
    
            /** @Logger () */ 
            this.log;
        }
        /** @Starter () */
        start(){
            console.log('Component/Service/Controller Started.');
        }
    
        Websocket(){
            this.websocket.emit('send data to all');
    
            for(let item of this.websocket.sockets){
                let socketId = item[0];
    		    let socket = item[1];
                socket.emit('User Id :',socket.userId || 'Anonymous');
            }
        }
        Sequelize(){
            var param = {sDate: sDate, eDate: eDate};
            let s = `SELECT * FROM TEST`;
            let resultList = this.sequelize.query(s,{
                type:this.sequelize.QueryTypes.SELECT,raw:true,
                replacements: param
            });
        }
        Request(){
            try{
                var OPTIONS = {
                    uri: 'http://test.com/rest/testinsert',
                    method: 'PUT',	//POST
                    json: true,     //response type
                    headers: {
                        'Content-Type': 'application/json',
                        'x-token': "mytoken"
                    },
                    body: {
                        "id": "test_id",
                        "name": "test_name"
                    }
                };
                const response = this.request(OPTIONS);
                console.log(response);
                   
            }catch(e){
                console.log('ERROR :',e);
                //return e;     // throw exception
            }
        }
        Render(){
            const str = this.render(this.constants._root+"/templates/email.html", {
                param1: "title", param2: "contents", param3: "copyright"
            });
        }
        Route(){
            this.route.get('/some/url',function(req,res,next){
                res.send('this is dynamic router');
            });
        }
        Constants(){
            console.log(this.constants._root);  // the data in app.config.js
        }
        Encrypt(){
            let enc = this.encrypt.encode('testEncrypt');  
            console.log(enc);  //14ec1b40be3a75a12ae522cc38affab8
            console.log(this.encrypt.decode(enc)); //testEncrypt
        }
        Email(){
            try{
                let mailOptions = {
                    //from: '[email protected]',             //single
                    to: '[email protected],[email protected]',  //multiple
                    subject: 'some title',	                //title
                    html: '<p> some contents </p>'			//contents
                };
                let result = this.email.send(mailOptions);
                console.log("Finish sending email : " + result.response);
            }catch(e){
                console.log(e.response);
                //return e;     // throw exception
            }
        }
        Locale(){
            console.log(this.i18n.getLocale());		//read default 'en'
            this.i18n.setLocale('en');	//set lang
            res.cookie('lang', 'en'); 	//set lang - same as above
            // http://host/lang/en      //set lang - same as above
            console.log(this.i18n.__('home.title',{aa:'test'});  //translate back-end
               
            //<%= __('sample_title')/*Title*/%>     //translate front-end
            //Using the en.json file when call in the VUE
        }
        Logger(){
            this.log.debug('debug test');
            this.log.info('info test');
            this.log.error('error test');
        }
    }
           

    @Inject('Service or Model') @Service 또는 @Model객체를 변수에 할당
    @Websocket('/ns') socketIO객체를 가져옴 namespace(option)로 구분
    @Sequelize('') this.sequelize.query()로 query를 바로 호출한다.
    @Request 외부의 http url을 호출한다.
    @Render 메일 발송같은 임의로 ExpressJs Render(ejs parsing)를 한다.
    @Route ExpressJs 객체를 사용한다. (get,post,delete등을 동적 설정)
    @Constants 초기에 입력받은 상수 값을 사용 할 수 있다.(config)
    @Encrypt 문서의 암/복호화를 사용할 수 있다.
    @Email 초기에 입력받은 Smtp설정으로 간편하게 이메일을 사용 한다.(config)
    @Locale 다국어 객체를 받음. en.json,ko.json 등의 파일을 사용한다.(config)
    @Logger winston logger를 사용 (config)
    @Starter 시스템이 시작될때 Method를 onload해준다.
    @ForceAwait Class 상단에 사용하며 forceAwait == false 일때 자동으로 async와 await를 붙인다. @NoForceAwait Class 상단에 사용하며 forceAwait == true 일때 자동으로 async와 await를 붙이지 않는다.

  2. @Component File

    • 공통소스 , 배치 , Filter 등의 용도로 사용합니다.
    • class상단에 @Component를 추가해야합니다.
    • 모든 @Annotation은 Comment로 추가해야 합니다.
        /** @Component */
        export class FilterComponent{
            constructor(){}
    
            /** @Starter () */
            start(){
                console.log('component started.');
            }
            // @FilterMapping ('/*')
            SessionInterceptor(req,res,next){
                res.header('X-XSS-Protection' , 0 );
                console.log('페이지 호출전에 이 function을 선행 호출');
                return next();
            }
    
            // @Scheduler ('0 */10 * * * *')
            every10min(){
                console.log('10분마다 호출됨.');
            }
    
            // @Scheduler ('500')
            every10min(dt, d){
                console.log('0.5초 마다 호출됨.');
            }
    
            /** @Socket ('message','/ns') */
            socketMessage(data,socket){
    	        socket.userId = data.userId;
    
                console.log('Client에서 socket.emit("message",{}); 로 호출함');
                socket.emit('log','Client said : '+data);
                socket.broadcast.emit('message',data);      //나빼고 전체발송
            }
    
            /** @SocketConnect ('/ns') */
            connect(socket){
                console.log("socket connection : "+socket.id);
                const roomId = socket.request.session.roomId;   //session읽기
                return {roomId:roomId};
            }
               
            /** @SocketDisconnect ('/ns') */
            disconnect(socket, connectReturn){
                console.log(connectReturn);     //위connect에서 return한 값
                console.log("socket disconnected : "+socket.id);
            }
    
            /** @Cacheable ('key', '#vo.id') */
            getList(vo){
                return this.commonBoardMapper.getOptList(vo);
                   
            }
            /** @CacheEvict ('key', '#vo.id') */
            insertVo(vo){
                return true;
            }
            /** @CachePut ('key', '#vo.id') */
            updateVo(vo){
                return true;
            }
               
        }
    • Component에서 사용 할수 있는 Method용 Annotation목록 입니다.

      // @FilterMapping ('/*') : request 호출의 선행자로 동작
      // @Starter() : node실행할때 선행 실행된다.
      // @Scheduler ('0 */10 * * * *') : cron을 이용한 Scheduler
      // @Socket ('emit name',ns) : server에서 socket request처리, ns는 옵션
      // @SocketConnect (ns) : socket 사용자 접속시, ns는 옵션
      // @SocketDisconnect (ns) : socket 사용자 분리시, ns는 옵션
      // @Cacheable (key, '#param.id') : 캐시를 사용한다. 1회로드 하고 리턴받은 값을 다음 호출시 전달, ()는 Class+Method로 생성
      // @CacheEvict (key, '#param.id') : 캐시 삭제, (key)만 입력하면 하위 캐시 모두 제거
      // @CachePut (key, '#param.id') : 캐시 업데이트, 무조건 리턴값을 캐싱한다.

  3. @Controller File

    • Express router 설정을 annotation으로 진행 합니다.
    • class상단에 @Controller를 추가해야합니다.
    • 모든 @Annotation은 Comment로 추가해야 합니다.
        /** @Controller */
        /** @RequestMapping ('/v1') */
        export class LoginController{
            constructor(){}
               
            /** @Starter () */
            start(){
                console.log('controller started.');
            }
    
            /** @RequestMapping ('/login/:id',get) */
            login_get(req,res,next){
                let userid = req.params.id;
                res.render('account/login',{userid}); 
            }
            /** @RequestMapping ('/login',post) */
            login_post(req,res,next){
                try{
                    let userid = req.body.param1;
    
                    console.log("express 문법을 따릅니다.");
    
                    res.status(200).json({result:"SUCCESS"});   //put:201
                }catch(e){
                    res.status(500).json({result:e});
                }
            }
               
            /** 여러개 파일 업로드 (uploadPath필수)
             * @anno1 : 경로
             * @anno2 : method type (files 고정값) -> (req.files :  파일정보)  
             * @anno3 : input의 name attribute 
               예) <input type="file" name="image"/>
            */
            /** @RequestMapping ('/imagesubmit',files,'image') */
            imageuploadSubmit(req,res,next){
                try{
                    for(var i in req.files){
                        const fileInfo = req.files[i];
                        console.log('이미 uploadPath에 파일은 저장됨.');
                        console.log('파일정보.(filepath:/202101/20/xx)',fileInfo);
                        this.service.insertFileManager(fileInfo);
                    }
                       
                    res.json({result:'SUCCESS'});
                }catch(e){
                    console.log(e);
                    res.json({errorMsg:e});
                }
            }
               
            /** 1개파일만 업로드 (uploadPath필수)
             * @anno1 : 경로
             * @anno2 : method type (file 고정값) -> (req.file :  파일정보)  
             * @anno3 : input의 name attribute 
               예) <input type="file" name="layout"/>
            */
            /** @RequestMapping ('/imagesubmit',file,'layout') */
            singleFileUploadSubmit(req,res,next){
                try{
                    const fileInfo = req.file;
                    console.log('이미 uploadPath에 파일은 저장됨.');
                    console.log('파일정보.(filepath:/202101/20/xx)',fileInfo);
                    this.service.insertFileManager(fileInfo);
                    res.json({result:'SUCCESS'});
                }catch(e){
                    console.log(e);
                    res.json({errorMsg:e});
                }
            }
        }
    • Controller에서만 사용 할수 있는 Method용 Annotation목록 입니다.

      // @Starter() : node실행할때 선행 실행된다.
      // @RequestMapping ('/v1') : class 밖의 RequestMapping은 전역 경로
      // @RequestMapping ('/login',post) : 전역과 더해진 router
      ( method list: get, post, delete, put, patch, files, file, all, use )

  4. @Service File

    • Service 상호간의 Inject가 가능 하도록 설계된 모듈
    • class상단에 @Service를 추가해야합니다.
    • 모든 @Annotation은 Comment로 추가해야 합니다.
    • @Inject annotation으로 Component, Controller, Service에 추가 될수 있습니다.
    • @Inject 할때 Service또는 Model을 inject한다. Service는 2depth까지 Inject가능
        /** @Service */
        class CommBoardService{
            constructor(){
                /** @Inject ('CommBoardMapper') */
                this.commonBoardMapper;
            }
    
            /** @Starter () */
            start(){
                console.log('service started.');
            }
    
    
            // @Scheduler ('0 */10 * * * *')
            every10min(){
                console.log('10분마다 호출됨.');
            }
    
            // @Scheduler ('500')
            every10min(dt, d){
                console.log('서버가 시작되면서 0.5초 마다 호출됨.');
            }
    
            /**
            * Add List
            * @param vo : req.body
            */
            /** @Transactional () */    //default : 'REQUIRED'
            setList(vo){
                const cnt = this.commonBoardMapper.getCount(vo);
                return true;
            }
    
            /** @Transactional ('REQUIRES_NEW','mongoose') */
            setList(vo){
                //transaction type of mongodb
                this.commonBoardMapper.setList(vo, cnt);
                   
                return true;
            }
            /** @Transactional ('','sequelize') */
            setList(vo){
                //transaction type of sequelize
                this.commonBoardMapper.setOptList(vo, cnt);
                return true;
            }
    
            /** @Cacheable ('key', '#vo.id') */
            getList(vo){
                return this.commonBoardMapper.getOptList(vo);
                   
            }
            /** @CacheEvict ('key', '#vo.id') */
            insertVo(vo){
                return true;
            }
            /** @CachePut ('key', '#vo.id') */
            updateVo(vo){
                return true;
            }
               
        }
    • Service에서 사용 할수 있는 Method용 Annotation 입니다.

      // @Transactional (['REQUIRED','REQUIRES_NEW','NESTED','SUPPORTS','MANDATORY','NOT_SUPPORTED','NEVER'],['mongoose','sequlize']) : DB Transaction처리
      // @Starter() : node실행할때 선행 실행된다.
      // @Scheduler ('0 */10 * * * *') : cron을 이용한 Scheduler
      // @Socket ('emit name',ns) : server에서 socket request처리, ns는 옵션
      // @SocketConnect (ns) : socket 사용자 접속시, ns는 옵션
      // @SocketDisconnect (ns) : socket 사용자 분리시, ns는 옵션
      // @Cacheable (key, '#param.id') : 캐시를 사용한다. 1회로드 하고 리턴받은 값을 다음 호출시 전달, ()는 Class+Method로 생성
      // @CacheEvict (key, '#param.id') : 캐시 삭제, (key)만 입력하면 하위 캐시 모두 제거
      // @CachePut (key, '#param.id') : 캐시 업데이트, 무조건 리턴값을 캐싱한다.
      ( 여러 DB를 동시 사용시 mongoose, sequelize를 수동으로 지정 해야함 )

  5. @Model File

    • Database(mongodb) 연결설정 Annotation입니다.
    • class상단에 @Model를 추가해야합니다.
    • 모든 @Annotation은 Comment로 추가해야 합니다.
    • @Inject annotation으로 Component, Controller, Service에 추가 될수 있습니다.
    • mode항목에 sequelize 또는 mongodb를 입력해서 구분
    • Sequelize
        import {HetaSequelize} from 'hetamvc'
        /** @Model */
        export default class FileManager extends HetaSequelize{
            init(Sequelize, options) {
                //options.indexes = [{ unique: false, fields: ['fileGroup']}];
                return this.model('filemanager', {
                    id: {type: Sequelize.INTEGER.UNSIGNED, primaryKey: true, autoIncrement: true},
                    fileGroup: {type: Sequelize.INTEGER.UNSIGNED,defaultValue: 0},	
                    fileName: {type: Sequelize.STRING(200)},
                    filePath: {type: Sequelize.STRING(100)},
                    mimeType:  {type: Sequelize.STRING(20)},
                    fileSize: {type: Sequelize.INTEGER.UNSIGNED,defaultValue: 0},
                    createUser: {type: Sequelize.STRING(16), allowNull: true},
                    createdAt: {type: 'TIMESTAMP', defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), allowNull: false}
                    },
                    options
                );
            }
        }
    • MongoDB
        import {HetaMongoose} from 'hetamvc'
    
        /** @Model */
        export default class FileManager extends HetaMongoose{
            init(mongoose) {
                let schema = new mongoose.Schema({
                    id: { type: Number, required: true, unique: true },
                    fileGroup: {type: Number, default: 0},	
                    fileName: { type: String, required: true },
                    filePath: { type: String, required: true },
                    mimeType: { type: String, required: false },
                    fileSize: {type: Number,default: 0},
                    createUser: {type: String},
                    createdAt: { type : Date, default: Date.now } //timestamp
                },{
                    _id: true,
                    timestamps: true, 
                    collection: this.name //테이블명을 classname으로
                });
                   
                return this.model(this.name, schema);
            }
        }