├── .gitattributes ├── .gitignore ├── .travis.yml ├── CNAME ├── README.md ├── index.htm ├── index.html ├── login.htm ├── man ├── index.htm ├── login.htm └── sample.htm ├── priv ├── k6 │ ├── config.js │ └── main.js └── static │ ├── back.jpg │ ├── index.htm │ ├── login.htm │ ├── synrc.css │ └── websocket.svg ├── rebar.config ├── src ├── index.erl ├── login.erl ├── routes.erl ├── sample.app.src └── sample.erl ├── sys.config └── vm.args /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-detectable=false 2 | *.htm linguist-detectable=false 3 | *.css linguist-detectable=false 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ebin 2 | log 3 | logs 4 | .applist 5 | Mnesia.nonode@nohost 6 | deps 7 | .rebar 8 | review 9 | _build 10 | rebar.lock 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 21.3 4 | - 22.0 5 | - 23.0 6 | notifications: 7 | email: 8 | - maxim@synrc.com 9 | script: 10 | - "curl -fsSL https://raw.github.com/synrc/mad/master/mad > mad" 11 | - "chmod +x mad" 12 | - "./mad dep com" 13 | - "rebar3 dialyzer" 14 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | sample.n2o.dev 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SAMPLE: WebSocket Application 2 | ============================= 3 | 4 | [![Build Status](https://travis-ci.com/synrc/sample.svg?branch=master)](https://travis-ci.com/synrc/sample) 5 | 6 | NITRO/N2O pages that work on top of raw COWBOY/RANCH connections. 7 | 8 | Intro 9 | ----- 10 | 11 | SAMPLE application consist of two page modules `login` and `index`. 12 | It creates WebSocket connection to `n2o` server. 13 | The permanent example is accesible at https://sample.n2o.dev/index.htm. 14 | 15 | Index 16 | ----- 17 | 18 | Init event. 19 | Chat event. 20 | Client event. 21 | File Transfer event. 22 | 23 | Login 24 | ----- 25 | 26 | Channel init. 27 | Login event. 28 | 29 | Setup 30 | ----- 31 | 32 | To run review application just clone, build and run: 33 | 34 | ``` 35 | $ rebar3 get-deps 36 | $ rebar3 shell 37 | ``` 38 | 39 | Then open it in browser: 40 | 41 | ``` 42 | $ open http://localhost:8001/app/login.htm 43 | ``` 44 | 45 | Credits 46 | ------- 47 | * Brought with ❤ by N2O community 48 | -------------------------------------------------------------------------------- /index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Chat 8 | 9 | 10 |
11 | 12 |

room

13 | 14 |
15 |
16 |
17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | SAMPLE 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 |
23 | 24 |

SAMPLE WS

25 |
26 | 27 | 45 |
46 | 47 |
48 |

REBAR3

49 | 50 |
51 |
Picture 1. OTP tools
52 | 53 | $ git clone git://github.com/synrc/sample 54 | $ cd sample 55 | $ rebar3 shell 56 | 57 | $ open https://127.0.0.1:8001/app/index.htm 58 |
59 |
60 |
61 |

START

62 | 63 |
64 |
Picture 2. Minimal Configuration
65 | 66 | {deps, [{cowboy, ".*", {git, "git://github.com/voxoz/cowboy2", []}}, 67 | {nitro, ".*", {git, "git://github.com/synrc/nitro", []}}, 68 | {n2o, ".*", {git, "git://github.com/synrc/n2o", []}}, 69 | {kvs, ".*", {git, "git://github.com/synrc/kvs", []}}, 70 | {syn, ".*", {git, "git://github.com/ostinelli/syn", {tag,"2.1.1"}}} 71 | ]}. 72 | 73 |
74 | 75 |
76 |
Picture 3. Development Release
77 | 78 | Eshell V10.1.1 (abort with ^G) 79 | 1> application:which_applications(). 80 | [{sample,"SAMPLE WebSocket application","0.7.0"}, 81 | {nitro,"NITRO HTML5 DSL","3.10"}, 82 | {syn,"A global Process Registry and Process Group manager.","2.1.1"}, 83 | {cowboy,"Small, fast, modern HTTP server.","2.8.0"}, 84 | {ranch,"Socket acceptor pool for TCP protocols.","1.7.1"}, 85 | {ssl,"Erlang/OTP SSL application","10.1"}, 86 | {public_key,"Public key infrastructure","1.9.1"}, 87 | {kvs,"KVS Abstract Chain Database","7.9.1"}, 88 | {cowlib,"Support library for manipulating Web protocols.","2.9.1"}, 89 | {crypto,"CRYPTO","4.8"}, 90 | {asn1,"The Erlang ASN1 compiler version 5.0.14","5.0.14"}, 91 | {n2o,"N2O MQTT TCP WebSocket","7.10.0"}, 92 | {inets,"INETS CXC 138 49","7.3"}, 93 | {mnesia,"MNESIA CXC 138 12","4.18"}, 94 | {syntax_tools,"Syntax tools","2.1.6"}, 95 | {compiler,"ERTS CXC 138 10","7.2.6"}, 96 | {stdlib,"ERTS CXC 138 10","3.13.2"}, 97 | {kernel,"ERTS CXC 138 10","7.1"}] 98 | 99 |
100 |
101 | 102 |
103 |

Sample Application

104 |
105 |
Picture 3. N2O Review Application
106 | 107 |
108 |
109 | 110 |
111 |

Related Documents

112 |
113 |
119 |
120 |
121 | 122 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /login.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Login 8 | 9 |
10 | 11 | 12 |
13 |

14 | 15 | 16 | 17 | 18 |

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /man/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | INDEX 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 |
23 | 24 |

INDEX

25 |
26 | 27 |
28 |
29 | 30 | 31 |

INTRO

32 | 33 |

Index page provides basic chat functionality and file upload feature.

34 | 35 |
36 |
37 | 38 |

PROTOCOL

39 | 40 |

event(init)

41 |

event(chat)

42 |

event(#client{})

43 |

event(#ftp{})

44 |

event(logout)

45 | 46 |
47 |
48 | 49 |

This module may refer to: 50 | 51 | sample, 52 | login. 53 |

54 | 55 |
56 |
57 | 58 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /man/login.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | LOGIN 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 |
23 | 24 |

LOGIN

25 |
26 | 27 |
28 |
29 | 30 | 31 |

INTRO

32 | 33 |

Login page providees nickname introduction and room selection.

34 | 35 |
36 |
37 | 38 |

PRTOCOL

39 | 40 |

event(init)

41 |

event(login)

42 | 43 |
44 |
45 | 46 |

This module may refer to: 47 | 48 | sample, 49 | index. 50 |

51 | 52 |
53 |
54 | 55 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /man/sample.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | SAMPLE 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 |
23 | 24 |

SAMPLE

25 |
26 | 27 |
28 |
29 | 30 | 31 |

INTRO

32 | 33 |

SAMPLE is a regular Erlang application.

34 | 35 |
36 |
37 | 38 |

HEADER

39 | 40 |
41 |
Picture 1. HOW TO SETUP BOTH APP AND SUP IN ERLANG
42 | 43 | -module(sample). 44 | -behaviour(supervisor). 45 | -behaviour(application). 46 | -export([init/1, start/0, start/2, stop/1, main/1]). 47 | 48 |
49 | 50 |
51 |
52 | 53 |

OTP

54 | 55 |

Note that suring start you should specify mauth module. 56 | Dafault implementation performs client topic autosubscription.

57 | 58 |
59 |
Picture 2. Implements Erlang/OTP service API
60 | 61 | start() -> start(normal,[]). 62 | start(_,_) -> supervisor:start_link({local,SAMPLE},SAMPLE,[]). 63 | stop(_) -> ok. 64 | 65 |
66 | 67 |
68 |
69 | 70 |

COWBOY

71 | 72 |

SAMPLE application uses COWBOY as a static HTTP server 73 | and as a WebSocket server.

74 | 75 |
76 |
Picture 3. COWBOY setup
77 | 78 | start(_,_) -> cowboy:start_tls(http,n2o_cowboy:env(?MODULE), 79 | #{env=>#{dispatch=>n2o_cowboy:points() }}), 80 | supervisor:start_link({local,sample},sample,[]). 81 | 82 |
83 | 84 |
85 |
86 | 87 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /priv/k6/config.js: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": { 3 | "local": "127.0.0.1" 4 | }, 5 | "stages": [ 6 | { 7 | "duration": "1m", 8 | "target": 10 9 | } 10 | ], 11 | "thresholds": { 12 | "http_req_duration": ["avg<100", "p(95)<200"] 13 | }, 14 | "noConnectionReuse": true, 15 | "type": "browser", 16 | "vus": "10", 17 | "duration": "3m", 18 | "userAgent": "MyK6UserAgentString/1.0" 19 | } 20 | -------------------------------------------------------------------------------- /priv/k6/main.js: -------------------------------------------------------------------------------- 1 | import { browser } from 'k6/experimental/browser'; 2 | import { check } from 'k6'; 3 | 4 | export const options = { 5 | scenarios: { ui: { executor: 'shared-iterations', options: { browser: { type: 'chromium' }, }, }, }, 6 | thresholds: { checks: ['rate==1.0'], }, 7 | }; 8 | 9 | export default async function () { 10 | const context = browser.newContext(); 11 | const page = context.newPage(); 12 | try { 13 | await page.goto('http://localhost:8001/app/login.htm'); 14 | const button = page.locator('#loginButton'); 15 | button.click(); 16 | await page.waitForNavigation(); 17 | check(page, { body: (p) => p.locator('#logout').textContent() === 'Logout ', }); 18 | } finally { 19 | page.close(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /priv/static/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synrc/sample/69eaa0dfe7ea5f3da68c64dec2b2a6efc10aa3a0/priv/static/back.jpg -------------------------------------------------------------------------------- /priv/static/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Chat 9 | 10 | 11 |
12 | 13 |

room

14 | 15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /priv/static/login.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Login 8 | 9 |
10 | 11 | 12 |
13 |

14 | 15 | 16 | 17 | 18 |

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /priv/static/synrc.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 9 | color: #737373; 10 | background: url('back.jpg') center / cover; 11 | margin: 0; 12 | } 13 | 14 | button { 15 | padding: 10px 20px; 16 | font-size: 18px; 17 | color: white; 18 | background: #4990E2; 19 | font-weight: bold; 20 | font-family: inherit; 21 | line-height: 1; 22 | border: none; 23 | text-transform: uppercase; 24 | border-radius: 4px; 25 | box-shadow: 0px 10px 15px -5px rgba(100, 100, 100, .35); 26 | margin: 10px; 27 | } 28 | 29 | button:hover { 30 | background: #1a84ff; 31 | } 32 | 33 | button:focus { 34 | outline: none; 35 | background: #1f63b2; 36 | } 37 | 38 | img { 39 | width: 140px; 40 | margin: auto; 41 | padding: 0; 42 | display: block; 43 | } 44 | 45 | .login { 46 | display: flex; 47 | flex-direction: column; 48 | height: 100vh; 49 | align-items: center; 50 | } 51 | 52 | .login h1 { 53 | color: #4990E2; 54 | font-size: 34px; 55 | margin: 30px 0; 56 | text-shadow: 1px 2px 8px rgba(100, 100, 100, .3); 57 | } 58 | 59 | .login img { 60 | margin-top: 20px; 61 | } 62 | 63 | .login form { 64 | width: 300px; 65 | background: white; 66 | padding: 20px; 67 | display: flex; 68 | justify-content: center; 69 | flex-wrap: wrap; 70 | border-radius: 4px; 71 | box-shadow: 0px 4px 40px rgba(100, 100, 100, .35); 72 | } 73 | 74 | .login label { 75 | width: 100%; 76 | display: block; 77 | font-size: 18px; 78 | } 79 | 80 | .login input { 81 | width: 100%; 82 | display: block; 83 | border: none; 84 | font-family: inherit; 85 | color: inherit; 86 | font-size: 19px; 87 | line-height: 1.4; 88 | border-bottom: 1px solid #4990E2; 89 | margin-bottom: 20px; 90 | } 91 | 92 | .login input:focus { 93 | outline: none; 94 | } 95 | 96 | .login footer { 97 | margin-top: auto; 98 | margin-bottom: 30px; 99 | font-size: 18px; 100 | background: white; 101 | padding: 4px 10px; 102 | border-radius: 4px; 103 | box-shadow: 0px 4px 40px rgba(100, 100, 100, .35); 104 | } 105 | 106 | .chat { 107 | text-align: center; 108 | } 109 | 110 | .chat header { 111 | display: inline-block; 112 | max-width: 200px; 113 | vertical-align: top; 114 | margin-right: 50px; 115 | margin-top: 20px; 116 | } 117 | 118 | .chat img { 119 | margin-bottom: 20px; 120 | } 121 | 122 | .chat h2 { 123 | color: #4990E2; 124 | font-size: 28px; 125 | margin: 10px 0; 126 | text-shadow: 1px 2px 8px rgba(100, 100, 100, .3); 127 | } 128 | 129 | .chat main { 130 | display: inline-block; 131 | vertical-align: top; 132 | text-align: left; 133 | max-width: 600px; 134 | margin: 40px 10px; 135 | } 136 | 137 | .chat form { 138 | display: flex; 139 | flex-wrap: wrap; 140 | justify-content: space-between; 141 | margin: 0px 0 70px; 142 | } 143 | 144 | .chat textarea:focus { 145 | outline: none; 146 | } 147 | 148 | .chat textarea { 149 | width: 100% !important; 150 | margin-bottom: 20px; 151 | border: none; 152 | font-family: inherit; 153 | color: inherit; 154 | font-size: 18px; 155 | line-height: 1.2; 156 | padding: 8px 16px; 157 | border-radius: 4px; 158 | box-shadow: 0px 4px 40px rgba(100, 100, 100, .35); 159 | } 160 | 161 | ::-webkit-input-placeholder { 162 | color: #ccc; 163 | } 164 | 165 | ::-moz-placeholder { 166 | color: #ccc; 167 | } 168 | 169 | ::-ms-input-placeholder { 170 | color: #ccc; 171 | } 172 | 173 | .chat span>button { 174 | margin-right: 5px; 175 | } 176 | 177 | history { 178 | display: block; 179 | } 180 | 181 | message { 182 | display: block; 183 | font-size: 18px; 184 | background: white; 185 | padding: 8px 16px; 186 | margin: 15px 0; 187 | border-radius: 4px; 188 | box-shadow: 0px 4px 40px rgba(100, 100, 100, .35); 189 | white-space: pre-wrap; 190 | overflow-x:auto; 191 | } 192 | 193 | author { 194 | display: inline; 195 | font-weight: bold; 196 | } 197 | 198 | author:empty { 199 | display: none; 200 | } 201 | 202 | author::after { 203 | content: ': '; 204 | } -------------------------------------------------------------------------------- /priv/static/websocket.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {deps, [{cowboy, "2.8.0"}, 2 | {syn,"2.1.1"}, 3 | {nitro, "7.4.1"}, 4 | {n2o,"8.12.1"}, 5 | {kvs, "9.4.2"}]}. 6 | 7 | {relx, [{release, {sample, "0.7.0"},[sample]}, 8 | {dev_mode, false}, 9 | {sys_config, "sys.config"}, 10 | {vm_args, "vm.args"}, 11 | {include_erts, true}, 12 | {extended_start_script, true}]}. 13 | 14 | {shell,[{config, "sys.config"}, 15 | {apps, [sample]}]}. 16 | 17 | {dialyzer, [ 18 | {plt_apps, top_level_deps} 19 | ]}. 20 | 21 | {project_plugins, [rebar3_format]}. 22 | {format, [ 23 | {files, ["src/*.erl", "test/*.erl"]}, 24 | {formatter, otp_formatter}, 25 | {options, #{ line_length => 108, 26 | paper => 250, 27 | spaces_around_fields => false, 28 | inlining => all, 29 | inline_clause_bodies => true, 30 | inline_expressions => true, 31 | inline_qualified_function_composition => true, 32 | inline_simple_funs => true, 33 | inline_items => all, 34 | inline_fields => true, 35 | inline_attributes => true 36 | }}]}. 37 | -------------------------------------------------------------------------------- /src/index.erl: -------------------------------------------------------------------------------- 1 | -module(index). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("nitro/include/nitro.hrl"). 6 | 7 | -include_lib("n2o/include/n2o.hrl"). 8 | 9 | -include_lib("kvs/include/cursors.hrl"). 10 | 11 | event(init) -> 12 | Room = n2o:session(room), 13 | Key = {topic, Room}, 14 | n2o:reg(Key), 15 | n2o:reg(n2o:sid()), 16 | nitro:clear(history), 17 | nitro:update(logout, 18 | #button{id = logout, body = "Logout " ++ n2o:user(), 19 | postback = logout}), 20 | nitro:update(heading, [#h2{id = heading, body = Room}]), 21 | nitro:update(upload, #upload{}), 22 | nitro:update(send, 23 | #button{id = send, body = <<"Chat">>, postback = chat, 24 | source = [message]}), 25 | [event(#client{data = E}) 26 | || E <- lists:reverse(kvs:feed(Key))]; 27 | event(logout) -> 28 | n2o:user([]), 29 | nitro:redirect("/app/login.htm"); 30 | event(chat) -> chat(nitro:q(message), nitro); 31 | event(#client{data = 32 | {'$msg', _, _, _, User, Message}}) -> 33 | nitro:wire(#jq{target = message, 34 | method = [focus, select]}), 35 | nitro:insert_top(history, 36 | nitro:render(#message{body = 37 | [#author{body = User}, Message]})); 38 | event(#ftp{sid = Sid, filename = Filename, 39 | status = {event, stop}} = 40 | Data) -> 41 | Name = 42 | hd(lists:reverse(string:tokens(nitro:to_list(Filename), 43 | "/"))), 44 | chat(nitro:render(#link{href = 45 | iolist_to_binary(["/app/", Sid, "/", Name]), 46 | body = Name}), 47 | index); 48 | event(Event) -> ok. 49 | 50 | jse(X) -> X. 51 | 52 | chat(Message, F) -> 53 | Room = n2o:session(room), 54 | User = n2o:user(), 55 | Msg = {'$msg', 56 | kvs:seq([], []), 57 | [], 58 | [], 59 | User, 60 | F:jse(Message)}, 61 | kvs:append(Msg, {topic, Room}), 62 | n2o:send({topic, Room}, #client{data = Msg}). 63 | -------------------------------------------------------------------------------- /src/login.erl: -------------------------------------------------------------------------------- 1 | -module(login). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("nitro/include/nitro.hrl"). 6 | 7 | -include_lib("n2o/include/n2o.hrl"). 8 | 9 | event(init) -> 10 | nitro:update(loginButton, 11 | #button{id = loginButton, body = "Login", 12 | postback = login, source = [user, pass]}); 13 | event(login) -> 14 | User = nitro:to_list(nitro:q(user)), 15 | Room = nitro:to_list(nitro:q(pass)), 16 | n2o:user(User), 17 | n2o:session(room, Room), 18 | nitro:redirect("/app/index.htm?room=" ++ Room), 19 | ok; 20 | event(_) -> []. 21 | -------------------------------------------------------------------------------- /src/routes.erl: -------------------------------------------------------------------------------- 1 | -module(routes). 2 | 3 | -include_lib("n2o/include/n2o.hrl"). 4 | 5 | -export([init/2, finish/2]). 6 | 7 | finish(State, Ctx) -> {ok, State, Ctx}. 8 | 9 | init(State, #cx{req = Req} = Cx) -> 10 | #{path := Path} = Req, 11 | {ok, 12 | State, 13 | Cx#cx{path = Path, module = route_prefix(Path)}}. 14 | 15 | route_prefix(<<"/ws/", P/binary>>) -> route(P); 16 | route_prefix(<<"/", P/binary>>) -> route(P); 17 | route_prefix(P) -> route(P). 18 | 19 | % Don't use fancy routers, be like poor man's pattern mach. 20 | 21 | route(<<>>) -> login; 22 | route(<<"index", _/binary>>) -> index; % github static 23 | route(<<"login", _/binary>>) -> login; % github static 24 | route(<<"app/index", _/binary>>) -> index; % priv static 25 | route(<<"app/login", _/binary>>) -> login; % priv static 26 | route(_) -> login. 27 | -------------------------------------------------------------------------------- /src/sample.app.src: -------------------------------------------------------------------------------- 1 | {application, sample, 2 | [{description, "SAMPLE WebSocket application"}, 3 | {vsn, "4.11.0"}, 4 | {registered, []}, 5 | {applications, [public_key,kernel,stdlib,crypto,kvs,mnesia,n2o,nitro,syn,cowlib,ssl,ranch,cowboy]}, 6 | {mod, { sample, []}}, 7 | {env, []}, 8 | {modules,[]}]}. 9 | -------------------------------------------------------------------------------- /src/sample.erl: -------------------------------------------------------------------------------- 1 | -module(sample). 2 | 3 | -behaviour(supervisor). 4 | 5 | -behaviour(application). 6 | 7 | -compile(export_all). 8 | 9 | main(A) -> mad:main(A). 10 | 11 | stop(_) -> ok. 12 | 13 | start() -> start(normal, []). 14 | 15 | start(_, _) -> 16 | cowboy:start_clear(http, 17 | [{port, application:get_env(n2o, port, 8001)}], 18 | #{env => #{dispatch => n2o_cowboy:points()}}), 19 | supervisor:start_link({local, sample}, sample, []). 20 | 21 | init([]) -> 22 | kvs:join(), 23 | {ok, {{one_for_one, 5, 10}, []}}. 24 | -------------------------------------------------------------------------------- /sys.config: -------------------------------------------------------------------------------- 1 | [ 2 | {n2o, [{port,8001}, 3 | {igor,"deps/n2o/src/protos"}, 4 | {app,sample}, 5 | {upload,"./priv/static/"}, 6 | {routes,routes}, 7 | {mq,n2o_syn}, 8 | {formatter,n2o_bert}, 9 | {protocols,[n2o_heart,nitro_n2o,n2o_ftp]}, 10 | {session,n2o_session}, 11 | {origin,<<"*">>}, 12 | {pickler,n2o_secret}, 13 | {event,pickle}]}, 14 | {kvs, [{dba,kvs_mnesia}, 15 | {dba_st,kvs_stream}, 16 | {schema, [kvs, kvs_stream ]} ]} 17 | ]. 18 | -------------------------------------------------------------------------------- /vm.args: -------------------------------------------------------------------------------- 1 | +W w 2 | -kernel net_ticktime 60 3 | -env ERL_CRASH_DUMP log/crash.dump 4 | +e 256000 5 | -env ERL_FULLSWEEP_AFTER 1000 6 | +Q 65536 7 | +P 256000 8 | +A 32 9 | +K true 10 | -smp auto 11 | -setcookie emq_dist_cookie 12 | -name sample-1@127.0.0.1 13 | +zdbbl 32768 14 | --------------------------------------------------------------------------------