2016년 11월 15일 화요일

process manager

http://pm2.keymetrics.io/

pm2 start app.js -x -- --prod
which http-server
pm2 start http-server --name app -- dist -p 80
https://github.com/AngularClass/angular2-webpack-starter/wiki/How-to-serve-with-PM2
https://saintcoder.wordpress.com/2014/05/29/deploying-sails-js-web-application-on-aws-ec2-instance/




forever start --minUptime 1000 --spinSleepTime 1000 app.js --port 1337

node forever 사용.



링크
https://saintcoder.wordpress.com/2014/05/29/deploying-sails-js-web-application-on-aws-ec2-instance/




forever start --minUptime 1000 --spinSleepTime 1000 app.js --port 1337

2015년 10월 1일 목요일

Node.js를 이용하여 iOS 푸시서비스 구현하기

서론

이번 프로젝트에서는 Springframework고 구현되어 있는 Push Provider를 nodejs로 마이그레이션하는 작업을 진행하였다. Provider는 일단 간단하게 푸시 전송할 데이터를 사내 데이터베이스 서버에서 RESTful API로 푸시 전송할 항목을 가져와서 Provider 서버의 데이트베이스에 저장하고 순차적으로 push를 전송하는 간단한 로직을 가지고 있는데 Springframework로 구현하니 단순히 Push 서비스만하는데 너무 큰 프레임워크를 도입한 것 같아서 보다 간단한 프레임워크 선정이 필요했고, async push provider를 구현하기 위해서 nodejs를 최종으로 결정해서 구현하기로 했다. 다른 내부적인 로직은 다른 포스팅에서 소개하기로 하고, nodejs 푸시서비스 구현하기 글에서는 Node.js로 아이폰과 안드로이드 Push Provider 서버를 구현하는 글을 소개한다. 첫번째로 아이폰 푸시서버 구현하기에서는 iOS 기계에 푸시를 전송하기 위해서 Push Provider 서버를 구현하는 방법을 간단히 소개한다.

iOS 프로젝트를 생성

데모를 보여주기 위해서 SFPushDemo 라는 이름으로 프로젝트를 만들었다. 단순하게 푸시가 아이폰으로 전송되는 것만 테스트할 것이기 때문에 빈 프로젝트로 만들었다.

Code Sign

Xcode는 버전이 올라가면서 눈에 띄는 업데이트가 많이 일어나는 것 같다. 이젠 Xcode 툴 자체에서 개발자 인증 및 프로비저닝 코드 싸인을 검사하고 keychain에 없을경우 웹에서 등록하고 추가하는 대신에 Xcode 자체에서 되도록 업데이트 된것 같다. 점점 웹 없이 Xcode 자체로 개발될 수 있는것 같은 느낌이 든다.
프로젝트의 code sign이 맞지 않을 겨우 No matching code signing identify found 에러가 나타나는데 이때, Fix issue를 누른다.
개발자 등록이 되어 있으면 개발자 계정을 Xcode에 추가하면 된다. 이번 포스팅에서 이 내용을 더 깊게 다루지는 않겠다. 다만 이렇게 Xcode가 많이 발전하고 있다는 소개를 잠시 했다.
다시 본론으로 돌아와서 아이폰 앱을 개발하기 위해서는 개발 프로비저닝 파일이 필요하고, 푸시를 보내기 위해서는 푸시 Cetificates 파일이 필요하다. 애플개발자 사이트에 들어가본다.

App Identifiers 추가

iOS 앱은 유일한 Identifier를 가지고 있고 이것을 이용해서 앱을 식별할 수 있게 된다. 푸시 데모 앱을 위해서net.saltfactory.tutorial 이라는 identifier를 만들었다. 여기서 identifier는 통상 도메인 이름을 거꾸로 적는다. 여러분들의 필요에 따라서 다른 이름으로 생성하면된다.

Provisioning Profile 추가

이제 Xcode에서 아이폰 개발을 할 때 사용할 provisioning profile을 추가한다. 추가하고 난 뒤 Download를 눌러 프로비저닝 파일을 다운 받아서 Xcode에 드래그해서 넣어주거나, 프로비저닝 파일을 더블클릭하면 자동적으로 Xcode에 추가된다.

iOS Certificates(Development) APNs Development iOS 추가

마지막으로 나중에 Push Provider 서버에 사용하기 위한 Certificates 파일을 추가한다. 나중에 푸시 프로바이더 서버를 구현할때 Cetificates 파일이 필요하다. 다운받아 둔다. apn_development.cer 이란 파일로 저장이 될 것이다.

AppDelegate.m에서 device token 획득

Xcode에서 AppDelegate.m 파일을 열어서 device token을 획득하는 코드를 추가한다.
//
//  SFAppDelegate.m
//  SFPushTutorial
//
//  Created by saltfactory@gmail.com on 1/14/14.
//  Copyright (c) 2014 saltfactory.net. All rights reserved.
//

#import "SFAppDelegate.h"

@implementation SFAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];

    // 푸시서비스 등록
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
                                        UIRemoteNotificationTypeAlert|
                                        UIRemoteNotificationTypeBadge|
                                        UIRemoteNotificationTypeSound];

    return YES;
}

// 디바이스 토큰 획득
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSLog(@"DeviceToken : %@", deviceToken );
}
이제 아이폰을 Xcode에 연결해서 빌드를 실행한다. 아이폰에서는 푸시서비스를 허용할 것인지 알람이 뜨고 확인을 하면 Xcode 콘솔에서 로그를 확인하면 디바이스 토큰 정보를 확인할 수 있다.
이제 모바일 디바이스에서는 푸시를 받을 준비를 다 했으니, 푸시 프로바이더 서버를 만들 차례이다.

node-apn 설치

node package는 npm을 이용해서 간단히 설치할 수 있다. 만약 맥을 사용하는데 npm이나 node 명령어가 없을 경우 homebrew를 이용해서 설치하면 간단하게 설치할 수 있다. 맥에서 Node.js는 (http://blog.saltfactory.net/197) 글을 참조해서 설치하면 된다. 푸시 프로바이더 서버는 node라는 디렉토리를 생성 후 그 밑에서 작업을 하였다.
npm install apn

APNs Certificates 파일과 인증키 생성

우린 앞에서 APNs Development Certificates 을 하나 생성해서 다운 받은 파일이 있다.aps_development.cer 이란 파일이다. 이 파일을 더블 클릭하면 Mac에서 key를 관리하는 KeyChain Access 라는 프로그램에 자동으로 등록이 된다. 열어서 확인해보자.
Apple Development iOS Push Service:net.saltfactory.tutorial 을 선택하여 오른쪽 마우스를 클릭하여 export를 한다. 그러면 Certificates.p12 파일로 저장이 될 것이다. 이름은 변경해도 무방하다.
파일을 저장할 때 인증 비밀번호를 물어보는데 비밀번호를 입력하고 기억해둔다.
위의 두 파일을 keys라는 디렉토리를 만들고 파일을 디렉토리에 복사를 한다.
node-apn은 pem 파일 포멧을 사용하기 때문에 다음과 같이 두 파일을 pem 파일로 변경한다.
openssl x509 -in aps_development.cer -inform DER -outform PEM -out cert.pem
openssl pkcs12 -in Certificates.p12 -out key.pem -nodes

푸시 프로바이더 구현

이제 푸시 프로바이더를 구현해보자. 소스코드는 간단하게 node-apn에서 제공하는 예제 코드를 이용하면 된다.
var apn = require('apn');

var options = {
        gateway : "gateway.sandbox.push.apple.com",
        cert: './keys/cert.pem',
        key: './keys/key.pem'
    };

var apnConnection = new apn.Connection(options);


var token = '앞에서 Xcode로 build 하면서 획득한 아이폰 디바이스 토큰을 입력한다.'
var myDevice = new apn.Device(token);

var note = new apn.Notification();
note.badge = 3;
note.alert = 'saltfactory 푸시 테스트';
note.payload = {'message': '안녕하세요'};

apnConnection.pushNotification(note, myDevice);
이제 이 코드를 실행시켜보자.
node sf_push_provider.js

결론

이번 포스팅에서는 node-apn을 이용해서 간단한 푸시 프로바이더를 구현하였고, 아이폰 디바이스에 푸시를 전송하는 방법을 소개하였다. 이번 포스팅에 사용된 Node.js 의 모듈은 node-apn을 사용하였다. 우리가 푸시 프로바이더를 동작하기 위해서는 간단한 몇줄의 코드와 인증파일을 생성하는 일이 전부였다. 물론 이렇게 간단한 코드로 서비스를 구현했다고 말하기 어렵다. 앞으로 많은 코드와 결합해서 더욱 멋진 푸시 프로바이더가 만들어질 것인데, APNs 서비르를 구현하기 위해서 가장 필요하면서도 복잡한 부분이 바로 푸시 프로바이더가 애플 푸시 서버로 디바이스정보와 함께 푸시 발송을 요청하는 부분인데 이 부분을 node-apn으로 할 수 있음을 소개하였다. 다음 포스팅은 node-gcm을 이용해서 안드로이드 디바이스에서 푸시 프로바이더를 구현하는 간단한 예제를 소개하도록 하겠다.


출처:http://blog.saltfactory.net/node/implementing-push-notification-service-for-ios.html

2015년 1월 13일 화요일

ionic a tag 클릭

myApp.filter('hrefToJS', function ($sce, $sanitize) { return function (text) { var regex = /href="([\S]+)"/g; var newString = $sanitize(text).replace(regex, "onClick=\"window.open('$1', '_blank', 'location=yes')\""); return $sce.trustAsHtml(newString); } });

android 中自动跳转到相应系统设置界面

android.provider.Settings。
1.   ACTION_ACCESSIBILITY_SETTINGS :    // 跳转系统的辅助功能界面
 
           Intent intent =  new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);  
           startActivity(intent);  
 
2.    ACTION_ADD_ACCOUNT :               // 显示添加帐户创建一个新的帐户屏幕。【测试跳转到微信登录界面】   
 
           Intent intent =  new Intent(Settings.ACTION_ADD_ACCOUNT);  
           startActivity(intent);
 
3.   ACTION_AIRPLANE_MODE_SETTINGS:       // 飞行模式,无线网和网络设置界面
 
           Intent intent =  new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS);  
           startActivity(intent);
 
        或者:
 
     ACTION_WIRELESS_SETTINGS  :      
 
                Intent intent =  new Intent(Settings.ACTION_WIFI_SETTINGS);  
                startActivity(intent);
 
4.    ACTION_APN_SETTINGS:                 //  跳转 APN设置界面
 
           Intent intent =  new Intent(Settings.ACTION_APN_SETTINGS);  
           startActivity(intent);
 
5.   【需要参数】 ACTION_APPLICATION_DETAILS_SETTINGS:   // 根据包名跳转到系统自带的应用程序信息界面   
 
               Uri packageURI = Uri.parse("package:" + "com.tencent.WBlog");
               Intent intent =  new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,packageURI);  
               startActivity(intent);
 
6.    ACTION_APPLICATION_DEVELOPMENT_SETTINGS :  // 跳转开发人员选项界面
 
           Intent intent =  new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);  
           startActivity(intent);
 
7.    ACTION_APPLICATION_SETTINGS :      // 跳转应用程序列表界面
 
           Intent intent =  new Intent(Settings.ACTION_APPLICATION_SETTINGS);  
           startActivity(intent);
 
       或者:
 
      ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS   // 跳转到应用程序界面【所有的】
 
             Intent intent =  new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS);  
             startActivity(intent);
 
       或者:
 
       ACTION_MANAGE_APPLICATIONS_SETTINGS  ://  跳转 应用程序列表界面【已安装的】
        
             Intent intent =  new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);  
             startActivity(intent);
 
 
 
8.    ACTION_BLUETOOTH_SETTINGS  :      // 跳转系统的蓝牙设置界面
 
           Intent intent =  new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);  
           startActivity(intent);
 
9.    ACTION_DATA_ROAMING_SETTINGS :   //  跳转到移动网络设置界面
 
           Intent intent =  new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);  
           startActivity(intent);
 
10.    ACTION_DATE_SETTINGS :           //  跳转日期时间设置界面
 
            Intent intent =  new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);  
            startActivity(intent);
 
11.    ACTION_DEVICE_INFO_SETTINGS  :    // 跳转手机状态界面
    
            Intent intent =  new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS);  
            startActivity(intent);
 
12.    ACTION_DISPLAY_SETTINGS  :        // 跳转手机显示界面
 
            Intent intent =  new Intent(Settings.ACTION_DISPLAY_SETTINGS);  
            startActivity(intent);
 
13.    ACTION_DREAM_SETTINGS     【API 18及以上 没测试】
 
            Intent intent =  new Intent(Settings.ACTION_DREAM_SETTINGS);  
            startActivity(intent);
 
 
14.    ACTION_INPUT_METHOD_SETTINGS :    // 跳转语言和输入设备
 
            Intent intent =  new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);  
            startActivity(intent);
 
15.    ACTION_INPUT_METHOD_SUBTYPE_SETTINGS  【API 11及以上】  //  跳转 语言选择界面 【多国语言选择】
 
             Intent intent =  new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);  
             startActivity(intent);
 
16.    ACTION_INTERNAL_STORAGE_SETTINGS         // 跳转存储设置界面【内部存储】
 
             Intent intent =  new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS);  
             startActivity(intent);
 
      或者:
 
        ACTION_MEMORY_CARD_SETTINGS    :   // 跳转 存储设置 【记忆卡存储】
 
             Intent intent =  new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS);  
             startActivity(intent);
 
  
17.    ACTION_LOCALE_SETTINGS  :         // 跳转语言选择界面【仅有English 和 中文两种选择】  
 
              Intent intent =  new Intent(Settings.ACTION_LOCALE_SETTINGS);  
              startActivity(intent);
 
 
18.     ACTION_LOCATION_SOURCE_SETTINGS :    //  跳转位置服务界面【管理已安装的应用程序。】
 
              Intent intent =  new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);  
              startActivity(intent);
 
19.    ACTION_NETWORK_OPERATOR_SETTINGS : // 跳转到 显示设置选择网络运营商。
 
              Intent intent =  new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);  
              startActivity(intent);
               
20.    ACTION_NFCSHARING_SETTINGS  :       // 显示NFC共享设置。 【API 14及以上】
 
              Intent intent =  new Intent(Settings.ACTION_NFCSHARING_SETTINGS);  
              startActivity(intent);
 
21.    ACTION_NFC_SETTINGS  :           // 显示NFC设置。这显示了用户界面,允许NFC打开或关闭。  【API 16及以上】
 
              Intent intent =  new Intent(Settings.ACTION_NFC_SETTINGS);  
              startActivity(intent);
 
22.    ACTION_PRIVACY_SETTINGS :       //  跳转到备份和重置界面
 
              Intent intent =  new Intent(Settings.ACTION_PRIVACY_SETTINGS);  
              startActivity(intent);
 
23.    ACTION_QUICK_LAUNCH_SETTINGS  : // 跳转快速启动设置界面
 
               Intent intent =  new Intent(Settings.ACTION_QUICK_LAUNCH_SETTINGS);  
               startActivity(intent);
 
24.    ACTION_SEARCH_SETTINGS    :    // 跳转到 搜索设置界面
 
               Intent intent =  new Intent(Settings.ACTION_SEARCH_SETTINGS);  
               startActivity(intent);
 
25.    ACTION_SECURITY_SETTINGS  :     // 跳转到安全设置界面
 
               Intent intent =  new Intent(Settings.ACTION_SECURITY_SETTINGS);  
               startActivity(intent);
 
26.    ACTION_SETTINGS   :                // 跳转到设置界面
 
                Intent intent =  new Intent(Settings.ACTION_SETTINGS);  
                startActivity(intent);
 
27.   ACTION_SOUND_SETTINGS                // 跳转到声音设置界面
  
                 Intent intent =  new Intent(Settings.ACTION_SOUND_SETTINGS);  
                 startActivity(intent);
 
28.   ACTION_SYNC_SETTINGS :             // 跳转账户同步界面
 
                Intent intent =  new Intent(Settings.ACTION_SYNC_SETTINGS);  
                startActivity(intent);
 
29.     ACTION_USER_DICTIONARY_SETTINGS :  //  跳转用户字典界面
 
                Intent intent =  new Intent(Settings.ACTION_USER_DICTIONARY_SETTINGS);  
                startActivity(intent);
 
30.     ACTION_WIFI_IP_SETTINGS  :         // 跳转到IP设定界面
 
                 Intent intent =  new Intent(Settings.ACTION_WIFI_IP_SETTINGS);  
                 startActivity(intent);
 
31.     ACTION_WIFI_SETTINGS  :            //  跳转Wifi列表设置
 
跳转方式
  Intent intent = new Intent(Settings.*********);
  startActivity(intent);

2014년 11월 27일 목요일

crypto-js를 이용한 javascript 암호화 및 복호화 예제

<head>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/core-min.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/components/pad-zeropadding.js"></script>
<!-- http:// 도메인/프로젝트 /WCMS_4121.jsp?pageNum=1 -->
<script>
$(document).ready(function(){
var key_hash = CryptoJS.MD5("Message");// 경기도평생 교육 진흥원에서 주는 키 (현재는 테스트 키 입니다.)
    var key = CryptoJS.enc.Utf8.parse(key_hash);
    var iv  = CryptoJS.enc.Utf8.parse('1234567812345678');// 해당 사이트 에서 임의의 키
   
$("table tr td").each(function(){
    $(this).html(dataEncode($(this).html()));//데이터 암호하기
});

//암호화
function dataEncode(message){
    var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding});
    return encrypted.toString();
}
//복호화
function dataDecode(encrypted){
    var decrypted = CryptoJS.AES.decrypt(encrypted,key,{iv:iv,padding:CryptoJS.pad.ZeroPadding});
    return decrypted.toString(CryptoJS.enc.Utf8);
}
});
</script>
</head>


참고 링크:

https://code.google.com/p/crypto-js/