├── .gitignore ├── README.md ├── integrated ├── 01_advanced_auth │ ├── README.md │ ├── bin │ │ ├── package.json │ │ ├── start_server.sh │ │ └── view │ │ │ ├── home │ │ │ └── main.html │ │ │ └── layout.html │ ├── server.hxml │ ├── src │ │ ├── Server.hx │ │ ├── Tasks.hx │ │ ├── api │ │ │ └── AuthApi.hx │ │ ├── auth │ │ │ ├── Auth.hx │ │ │ ├── AuthMiddleware.hx │ │ │ └── User.hx │ │ ├── controller │ │ │ └── HomeController.hx │ │ └── model │ │ │ ├── Managers.hx │ │ │ └── User.hx │ └── tasks.hxml └── 02_reactjs │ ├── README.md │ ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── empty.html │ │ ├── home │ │ ├── about.html │ │ └── main.html │ │ └── layout.html │ ├── client.hxml │ ├── server.hxml │ └── src │ ├── Client.hx │ ├── Server.hx │ ├── controller │ ├── HomeController.hx │ └── ReactResult.hx │ └── react │ └── App.hx └── minimal ├── 01_static_results ├── README.md ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── home │ │ ├── async.html │ │ └── main.html │ │ └── layout.html ├── server.hxml └── src │ ├── Server.hx │ └── controller │ └── HomeController.hx ├── 02_dynamic_results ├── README.md ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── home │ │ └── main.html │ │ └── layout.html ├── server.hxml └── src │ ├── Server.hx │ └── controller │ └── HomeController.hx ├── 03_file_upload ├── README.md ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── home │ │ └── main.html │ │ └── layout.html ├── server.hxml └── src │ ├── Server.hx │ └── controller │ └── HomeController.hx ├── 04_client_app ├── README.md ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── home │ │ ├── about.html │ │ └── main.html │ │ └── layout.html ├── client.hxml ├── server.hxml └── src │ ├── Client.hx │ ├── Server.hx │ └── controller │ └── HomeController.hx ├── 05_api ├── README.md ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── home │ │ └── main.html │ │ └── layout.html ├── server.hxml └── src │ ├── Server.hx │ ├── api │ ├── AnotherApi.hx │ └── FileApi.hx │ └── controller │ └── HomeController.hx ├── 06_basic_auth ├── README.md ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── home │ │ └── main.html │ │ └── layout.html ├── server.hxml └── src │ ├── Server.hx │ ├── api │ └── AuthApi.hx │ ├── auth │ ├── Auth.hx │ └── User.hx │ └── controller │ └── HomeController.hx ├── 07_remoting ├── README.md ├── bin │ ├── package.json │ ├── start_server.sh │ └── view │ │ ├── home │ │ ├── about.html │ │ └── main.html │ │ └── layout.html ├── client.hxml ├── server.hxml └── src │ ├── Client.hx │ ├── Server.hx │ ├── api │ ├── ApiContext.hx │ └── TestApi.hx │ └── controller │ └── HomeController.hx ├── 08_tasks ├── README.md ├── bin │ ├── package.json │ └── start_server.sh ├── src │ ├── Tasks.hx │ └── api │ │ └── FileApi.hx └── tasks.hxml └── 09_middleware └── .wip /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | uf-content/ 3 | npm-debug.log 4 | server.js* 5 | client.js* 6 | tasks.js* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Warning:** Ufront is not actively maintained now (as at Mar 2018). I highly recommend you to use [tink_web](https://github.com/haxetink/tink_web) instead 2 | 3 | # Ufront Guide for NodeJS target 4 | 5 | This is a collection of guides for the NodeJS target of Ufront ([website](http://ufront.net), [repo](https://github.com/ufront)). 6 | 7 | ## Overview 8 | 9 | #### What is Ufront? 10 | 11 | From Ufront repo: 12 | 13 | > Ufront is a client/server MVC web framework for Haxe. It allows you to share models, views and controllers between both server-side and client-side code. Client side, your app will run as a fast single-page-app. But everything can still work server-side - which makes for fast first page loads, great SEO, and a good fallback for old browsers. 14 | 15 | #### Why NodeJS only? 16 | 17 | Actually, Ufront does support the neko and php target (and they work very well). 18 | But according to my practical experience, the amount of libraries of nodejs (from npm) is much much more than what you can find for neko or php. 19 | That makes development much easier and faster. (p.s. for neko, you are basically limited to only use Haxe libraries). So, let's focus on NodeJS! 20 | 21 | #### Compile for neko/php (if you insist) 22 | 23 | Since Ufront does support neko and php, so the guides here should also apply to these two targets, with slight modification. The main difference is the entry point. 24 | In nodejs, we call `listen(port)` to start the server, and in neko/php we call `executeRequest()`. 25 | So, by changing this call you should be able to compile most of the examples in this guide. 26 | 27 | #### Repo structure 28 | 29 | The `minimal` folder contains a set of minimal examples. 30 | The scopes of these minimal examples are very confined, so readers can easily 31 | identify the minimum code required for a certain feature. 32 | 33 | The `integrated` folder contains a set of more-real-world examples that integrates 34 | several features. These examples are usually more complex and maybe a little bit 35 | harder to understand, make sure you have gone through the minimal examples first, 36 | to gain a brief understanding of the individual features. 37 | 38 | #### Feedback 39 | 40 | Please feel free to open issues/PR for: 41 | - Example request 42 | - Bug report / bug fixes 43 | - Any other general feedback / questions 44 | 45 | Or you can join our chatroom: https://gitter.im/ufront/ufront 46 | 47 | 48 | ## Usage 49 | 50 | The following are the steps to build and run the examples. 51 | 52 | ### Prepare 53 | 54 | ##### Haxe 55 | 56 | Install the required libraries. 57 | 58 | ``` 59 | haxelib git ufront-mvc https://github.com/ufront/ufront-mvc 60 | haxelib install express 61 | haxelib install hxnodejs 62 | haxelib install pushstate 63 | ``` 64 | 65 | ##### NodeJS 66 | 67 | 0. You need to install NodeJS on your machine, please refer to the official NodeJS guide ([website](https://nodejs.org)). 68 | 0. Install the "nodemon" package, `npm install -g nodemon`. 69 | 70 | ### Build 71 | 72 | Navigate to each example folder, then build the hxml files there, for example: 73 | 74 | ``` 75 | cd minimal/01_static_results 76 | haxe server.hxml 77 | ``` 78 | 79 | If there is a client, compile it as well: 80 | ``` 81 | haxe client.hxml 82 | ``` 83 | 84 | ### Run 85 | 86 | By default: Ufront requires the following node packages: 87 | - compression 88 | - body-parser 89 | - cookie-parser 90 | - express 91 | - multer 92 | 93 | The dependencies have been specified in the `package.json` file in each examples, so simply navigate to the `bin` folder, install the node dependencies by `npm install`. 94 | 95 | ``` 96 | cd bin 97 | npm install 98 | ``` 99 | 100 | Then start the server: 101 | 102 | ``` 103 | npm start 104 | ``` 105 | 106 | ### View 107 | 108 | Open your browser and navigate to `localhost:2987` 109 | -------------------------------------------------------------------------------- /integrated/01_advanced_auth/README.md: -------------------------------------------------------------------------------- 1 | # Integrated example: Advanced Authentication 2 | 3 | In this example, we will set up a server that will handle login, check credentials with database and persist login status across requests. 4 | 5 | ## Overview 6 | 7 | #### Database 8 | 9 | We will use MongoDB as our database backend in this example. 10 | Haxe's SPOD is a good library but it is not (yet?) supported on nodejs. 11 | For simplicity we will use the mongoose library as our database driver. 12 | 13 | #### Persistent Login Status 14 | 15 | We will use Json Web Tokens to persist login status. 16 | 17 | 18 | ## Prepare 19 | 20 | #### Install MongoDB 21 | 22 | Please refer to MongoDB official guide. 23 | To test if MongoDB is installed successfully, run the following in terminal: 24 | ```bash 25 | mongo 26 | ``` 27 | 28 | If you can enter the mongo shell then you are done. 29 | 30 | 31 | #### More Haxe Dependencies 32 | 33 | In addition to the libraries listed in the main README, we need js-kit and jsonwebtoken: 34 | ```bash 35 | haxelib git js-kit https://github.com/clemos/haxe-js-kit 36 | haxelib install hxnodejs-jsonwebtoken 37 | ``` 38 | 39 | 40 | ## Build 41 | 42 | Build server.hxml and tasks.hxml 43 | 44 | ```bash 45 | haxe server.hxml && haxe tasks.hxml 46 | ``` 47 | 48 | ## Run 49 | 50 | ```bash 51 | # cd to bin/ folder 52 | cd bin 53 | # install node dependencies 54 | npm install 55 | # create an user in database 56 | node tasks.js --create-user 57 | # start the web server 58 | npm start 59 | ``` -------------------------------------------------------------------------------- /integrated/01_advanced_auth/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01_advanced_auth", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0", 11 | "mongoose": "^4.3.3", 12 | "jsonwebtoken": "^5.5.4" 13 | }, 14 | "scripts": { 15 | "start": "./start_server.sh" 16 | } 17 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /integrated/01_advanced_auth/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 | ::if isLoggedIn:: 2 |

3 | You are logged in. Now try refreshing the page, it keeps you logged in. 4 |

5 |
6 | 7 |
8 | ::else:: 9 |

10 | You are NOT logged in. 11 |

12 |

Log in here: (username: ufront, password: ufrontpw)

13 |
14 | 15 | 16 | 17 |
18 | ::end:: 19 | -------------------------------------------------------------------------------- /integrated/01_advanced_auth/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 | ::viewContent:: 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /integrated/01_advanced_auth/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # jsonwebtoken 11 | -lib hxnodejs-jsonwebtoken 12 | 13 | # js-kit (for the mongoose externs) 14 | -lib js-kit 15 | 16 | # we are compiling for the server 17 | -D server 18 | 19 | # usual hxml things... 20 | -cp src 21 | -main Server 22 | -js bin/server.js -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import auth.Auth; 4 | import auth.AuthMiddleware; 5 | import controller.HomeController; 6 | import model.Managers; 7 | 8 | using ufront.MVC; 9 | 10 | class Server 11 | { 12 | static function main() 13 | { 14 | var ufrontApp = new UfrontApplication({ 15 | indexController: HomeController, 16 | defaultLayout: "layout.html", 17 | authImplementation: Auth, // specify our auth implementation 18 | requestMiddleware: [new AuthMiddleware()], // our auth middleware will check, when a request arrives, for a login access token 19 | }); 20 | 21 | // init database 22 | Managers.init(ufrontApp.injector); 23 | 24 | // listen for incoming connection at this port number 25 | ufrontApp.listen(2987); 26 | } 27 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/Tasks.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import api.*; 4 | import ufront.tasks.UFTaskSet; 5 | import model.User; 6 | import model.Managers; 7 | 8 | using ufront.MVC; 9 | using tink.CoreApi; 10 | 11 | class Tasks extends UFTaskSet 12 | { 13 | static function main() 14 | { 15 | var tasks = new Tasks(); 16 | 17 | // Inject our content directory in case we need to write to it. 18 | tasks.injector.map(String, "contentDirectory").toValue("../uf-content/"); 19 | 20 | // init database 21 | Managers.init(tasks.injector); 22 | 23 | // Inject all our APIs. 24 | for (api in CompileTime.getAllClasses(UFApi)) tasks.injector.mapRuntimeTypeOf(api).toSingleton(api); 25 | 26 | // Use CLI logging, which prints both to a log file and to the CLI output. 27 | tasks.useCLILogging("logs/tasks.log"); 28 | 29 | // Execute the task runner 30 | var args = Sys.args(); 31 | tasks.execute(args); 32 | 33 | } 34 | 35 | @inject @:skip 36 | public var userManager:UserManager; 37 | 38 | /** 39 | Create a user in database 40 | 41 | Usage: node tasks.js --create-user 42 | **/ 43 | public function createUser() 44 | { 45 | userManager.create({username: 'ufront', password: 'ufrontpw'}, function(err, user) { 46 | if(err != null) trace(err); 47 | Sys.exit(0); 48 | }); 49 | } 50 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/api/AuthApi.hx: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | import auth.Auth; 4 | import model.User; 5 | import js.npm.JsonWebToken; 6 | 7 | using ufront.MVC; 8 | using tink.CoreApi; 9 | 10 | class AuthApi extends UFApi 11 | { 12 | static inline var SECRET = @:privateAccess auth.AuthMiddleware.SECRET; 13 | 14 | // inject our database manager 15 | @inject 16 | public var userManager:UserManager; 17 | 18 | // inject the http context so that we can write a cookie later 19 | @inject 20 | public var context:HttpContext; 21 | 22 | public function login(username:String, password:String):Surprise 23 | { 24 | // database call is async, we use a future to handle it 25 | var trigger = Future.trigger(); 26 | 27 | // find the user in database 28 | userManager.findOne({username: username}, function(err, user) trigger.trigger(err == null ? Success(user) : Failure(Error.withData('Mongo Error', err)))); 29 | 30 | // check the credentials when the user data is fetched from database 31 | return trigger.asFuture() >> 32 | function(user:User) 33 | { 34 | // invalid username 35 | if(user == null) return new Error("User not found").asBadSurprise(); 36 | 37 | // incorrect password 38 | if(password != user.password) return new Error("Incorrect password").asBadSurprise(); 39 | 40 | // cast the auth interface into our implementation 41 | var authImplementation = Std.instance(auth, Auth); 42 | 43 | // initialize our auth handler 44 | authImplementation.init(username); 45 | 46 | // let the browser store the json web token as cookie 47 | // so that in the next request we can pick it up in the middleware, 48 | // in order to persist the login status 49 | var token = JsonWebToken.sign({username: username}, SECRET, {expiresIn: '30m'}); 50 | context.response.setCookie(new HttpCookie("access_token", token, Date.fromTime(Date.now().getTime() + 30 * 60 * 1000), null, "/")); 51 | return SurpriseTools.success(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/auth/Auth.hx: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | using ufront.MVC; 4 | 5 | class Auth implements UFAuthHandler 6 | { 7 | // required by the interface 8 | public var currentUser(get, never):UFAuthUser; 9 | 10 | // actually stores the user; 11 | var _currentUser:User; 12 | 13 | // initialize our auth handler with an user id, 14 | // this user id should be the result of some authentication (e.g. username/password) 15 | public function init(payload:Dynamic) 16 | { 17 | _currentUser = new User(payload.userId); 18 | } 19 | 20 | // required by the interface 21 | public function hasPermission (permission:EnumValue):Bool 22 | { 23 | return isLoggedIn() && _currentUser.can(permission); 24 | } 25 | 26 | // required by the interface 27 | public function hasPermissions (permissions:Iterable):Bool 28 | { 29 | return isLoggedIn() && _currentUser.can(permissions); 30 | } 31 | 32 | // required by the interface 33 | public function isLoggedIn():Bool 34 | { 35 | return _currentUser != null; 36 | } 37 | 38 | // required by the interface 39 | public function isLoggedInAs(user:UFAuthUser):Bool 40 | { 41 | return isLoggedIn() && _currentUser.userID == user.userID; 42 | } 43 | 44 | // required by the interface 45 | public function requireLogin ():Void 46 | { 47 | if(!isLoggedIn()) throw HttpError.authError(ANotLoggedIn); 48 | } 49 | 50 | // required by the interface 51 | public function requireLoginAs (user:UFAuthUser):Void 52 | { 53 | if(!isLoggedInAs(user)) throw HttpError.authError(ANotLoggedInAs(user)); 54 | } 55 | 56 | // required by the interface 57 | public function requirePermission (permission:EnumValue):Void 58 | { 59 | if(!hasPermission(permission)) throw HttpError.authError(ANoPermission(permission)); 60 | } 61 | 62 | // required by the interface 63 | public function requirePermissions (permissions:Iterable):Void 64 | { 65 | for(permission in permissions) if(!hasPermission(permission)) throw HttpError.authError(ANoPermission(permission)); 66 | } 67 | 68 | // required by the interface 69 | public function toString ():String 70 | { 71 | return "Auth"; 72 | } 73 | 74 | function get_currentUser() return _currentUser; 75 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/auth/AuthMiddleware.hx: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | import haxe.Json; 4 | import js.npm.JsonWebToken; 5 | import auth.Auth; 6 | 7 | using ufront.MVC; 8 | using tink.CoreApi; 9 | 10 | class AuthMiddleware implements UFRequestMiddleware 11 | { 12 | #if server // make sure our secret is not leaked to client 13 | static inline var SECRET = 'our-jwt-secret'; 14 | #end 15 | 16 | public function new() {} 17 | 18 | /** 19 | This function will be run when a request arrives, before it is handled by controllers. 20 | We look for a access token in the request and use it to identify the user. 21 | If an user is identified we initiate our auth handler, so that later when a controller 22 | handles this request, it can use `context.auth` to find out the auth-related information. 23 | **/ 24 | public function requestIn( ctx:HttpContext ):Surprise 25 | { 26 | // try to fetch the access token from cookies or post/get params 27 | var token = ctx.request.params['access_token']; 28 | 29 | if (token != null) 30 | { 31 | // try to decode the token 32 | var payload = 33 | try 34 | JsonWebToken.verify(token, SECRET) 35 | catch(e:js.Error) 36 | // there are some problem with the token, let's skip the following process 37 | return SurpriseTools.success(); 38 | 39 | // refresh the token to make it not to expire in the next 30 minutes 40 | // this is done through 41 | // - setting the expiry of the token (encoded in jwt), and; 42 | // - setting the expiry of the cookie 43 | var refreshedToken = JsonWebToken.sign(payload, SECRET, {expiresIn: '30m'}); 44 | ctx.response.setCookie(new HttpCookie("access_token", refreshedToken, Date.fromTime(Date.now().getTime() + 30 * 60 * 1000), null, "/")); 45 | 46 | // init our auth handler 47 | var auth:Auth = cast ctx.auth; 48 | auth.init(payload.username); 49 | } 50 | 51 | return SurpriseTools.success(); 52 | } 53 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/auth/User.hx: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | using ufront.MVC; 4 | 5 | class User implements UFAuthUser 6 | { 7 | // required by the interface 8 | public var userID(get, never):String; 9 | 10 | // actually stores the user id 11 | var userId:String; 12 | 13 | public function new(userId:String) 14 | { 15 | this.userId = userId; 16 | } 17 | 18 | // required by the interface 19 | public function can(?permission:EnumValue, ?permissions:Iterable):Bool 20 | { 21 | // for minimal demonstration purpose, here simply returns true to allow all permissions. 22 | // in reality, you should implement your own logic (e.g. query database, etc) 23 | // to determine if an user is "can" do the specified permissions 24 | return true; 25 | } 26 | 27 | function get_userID() return userId; 28 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import api.AuthApi; 4 | 5 | using ufront.MVC; 6 | 7 | class HomeController extends Controller 8 | { 9 | @inject 10 | public var authApi:AuthApi; 11 | 12 | @:route(GET, "/") 13 | public function main() 14 | { 15 | var isLoggedIn = context.auth.isLoggedIn(); 16 | 17 | return new ViewResult({ 18 | title: "Ufront NodeJS Guide", 19 | isLoggedIn: isLoggedIn, 20 | }); 21 | } 22 | 23 | @:route(POST, "/login") 24 | public function login(args:{username:String, password:String}) 25 | { 26 | // try to login and redirect back to home page if success 27 | return authApi.login(args.username, args.password) >> 28 | function(_) return new RedirectResult('/'); 29 | } 30 | 31 | @:route(POST, "/logout") 32 | public function logout() 33 | { 34 | // clear the access_token cookie in user's browser 35 | context.response.setCookie(new HttpCookie("access_token", '', Date.now(), null, "/")); 36 | 37 | // redirect back to home page 38 | return new RedirectResult('/'); 39 | } 40 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/model/Managers.hx: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | import js.npm.mongoose.Mongoose; 4 | import model.User; 5 | 6 | class Managers 7 | { 8 | public static function init(injector:minject.Injector) 9 | { 10 | // connect to mongodb 11 | var mongoose = new Mongoose(); 12 | mongoose.connect("mongodb://localhost/01_advanced_auth"); 13 | 14 | // init our database manager 15 | var userManager = UserManager.build(mongoose, "User"); 16 | 17 | // inject our user manager 18 | injector.map(UserManager).toValue(userManager); 19 | } 20 | } -------------------------------------------------------------------------------- /integrated/01_advanced_auth/src/model/User.hx: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | // schema for our User model in database 4 | typedef UserData = 5 | { 6 | username:String, 7 | password:String, 8 | } 9 | 10 | // collection manager 11 | class UserManager extends js.npm.mongoose.macro.Manager {} 12 | 13 | @:schemaOptions({ 14 | autoIndex: true 15 | }) 16 | class User extends js.npm.mongoose.macro.Model {} -------------------------------------------------------------------------------- /integrated/01_advanced_auth/tasks.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # the uftasks library 5 | -lib ufront-uftasks 6 | 7 | # we need hxnodejs because we are targeting nodejs 8 | -lib hxnodejs 9 | 10 | # ufront uses expressjs behind the hood 11 | -lib express 12 | 13 | # js-kit (for the mongoose externs) 14 | -lib js-kit 15 | 16 | # we are compiling for the server 17 | -D server 18 | 19 | # usual hxml things... 20 | -cp src 21 | -main Tasks 22 | -js bin/tasks.js -------------------------------------------------------------------------------- /integrated/02_reactjs/README.md: -------------------------------------------------------------------------------- 1 | # Integrated example: Using React with Ufront 2 | 3 | In this example, we will set up a client application that will use React (http://reactjs.org/). 4 | 5 | ## Prepare 6 | 7 | #### More Haxe Dependencies 8 | 9 | In addition to the libraries listed in the main README, we need the react library: 10 | ```bash 11 | haxelib git react https://github.com/massiveinteractive/haxe-react 12 | ``` -------------------------------------------------------------------------------- /integrated/02_reactjs/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "02_reactjs", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /integrated/02_reactjs/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /integrated/02_reactjs/bin/view/empty.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinresol/ufront-nodejs-guide/a4b92bfd7d8257388ab698fa90af4643dbb8ef7b/integrated/02_reactjs/bin/view/empty.html -------------------------------------------------------------------------------- /integrated/02_reactjs/bin/view/home/about.html: -------------------------------------------------------------------------------- 1 |

2 | About Page... 3 |

-------------------------------------------------------------------------------- /integrated/02_reactjs/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 |

2 | ::message:: 3 |

-------------------------------------------------------------------------------- /integrated/02_reactjs/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 |
20 | ::viewContent:: 21 |
22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /integrated/02_reactjs/client.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # use the pushstate lib, which handles the browser history for us 5 | -lib pushstate 6 | 7 | # the haxe-react library 8 | -lib react 9 | 10 | # we are compiling for the client 11 | -D client 12 | 13 | # we are not going to use js require 14 | -D react_global 15 | 16 | # usual hxml things... 17 | -cp src 18 | -main Client 19 | -js bin/client.js -------------------------------------------------------------------------------- /integrated/02_reactjs/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /integrated/02_reactjs/src/Client.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | import minject.Injector; 5 | import react.App; 6 | using ufront.MVC; 7 | 8 | class Client 9 | { 10 | public static var reactApp:App; 11 | public static var ufrontApp:ClientJsApplication; 12 | public static var injector:Injector; 13 | 14 | static function main() 15 | { 16 | ufrontApp = new ClientJsApplication({ 17 | indexController: HomeController, 18 | defaultLayout: "layout.html", 19 | }); 20 | 21 | // Listen to any history changes using PushState, and process each request. 22 | ufrontApp.listen(); 23 | 24 | // trigger the initial render 25 | ufrontApp.executeRequest(); 26 | } 27 | } -------------------------------------------------------------------------------- /integrated/02_reactjs/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Server 7 | { 8 | static function main() 9 | { 10 | var ufrontApp = new UfrontApplication({ 11 | indexController: HomeController, // all incoming requests will be handled by this controller 12 | defaultLayout: "layout.html", // use this file as the default layout for a ViewResult 13 | }); 14 | 15 | // listen for incoming connection at this port number 16 | ufrontApp.listen(2987); 17 | } 18 | } -------------------------------------------------------------------------------- /integrated/02_reactjs/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | #if client 4 | import api.react.ReactMacro.jsx; 5 | #end 6 | 7 | using ufront.MVC; 8 | 9 | class HomeController extends Controller 10 | { 11 | @post 12 | public function postInject() 13 | { 14 | ViewResult.globalValues['title'] = "Ufront with React"; 15 | } 16 | 17 | /** 18 | Serves a main page. 19 | On server-side it renders nothing but only send an almost-empty layout html plus the client js app to browser. 20 | Then on client-side it will render the react components. 21 | **/ 22 | @:route(GET, "/") 23 | public function main() 24 | { 25 | return new ReactResult(jsx(' 26 |
27 |

Home

28 | About Me 29 |
30 | ')); 31 | } 32 | 33 | /** 34 | Serves another page 35 | **/ 36 | @:route(GET, "/about") 37 | public function about() 38 | { 39 | return new ReactResult(jsx(' 40 |
41 |

About

42 | Home Page 43 |
44 | ')); 45 | } 46 | 47 | 48 | #if server 49 | // we are not going to do anything with jsx on server-side 50 | public static inline function jsx(_) return null; 51 | #end 52 | } 53 | -------------------------------------------------------------------------------- /integrated/02_reactjs/src/controller/ReactResult.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | using ufront.MVC; 4 | using tink.CoreApi; 5 | 6 | #if client 7 | 8 | import api.react.ReactMacro.jsx; 9 | import api.react.ReactDOM; 10 | import react.App; 11 | 12 | /** 13 | We need a way to trigger a re-render in our react app. 14 | Here we use a PartialViewResult with ClientAction to do that. 15 | **/ 16 | abstract ReactResult(AddClientActionResult) to AddClientActionResult 17 | { 18 | public function new(content:Dynamic) 19 | this = new PartialViewResult({}, "../empty.html").addClientAction(RenderPageAction, {content: content}); 20 | } 21 | 22 | class RenderPageAction extends UFClientAction 23 | { 24 | override public function execute(httpContext:HttpContext, ?data:RenderPageActionData) 25 | { 26 | // store the current injector for use in react components 27 | Client.injector = httpContext.injector; 28 | 29 | // create and mount the react app if not yet done so 30 | if(Client.reactApp == null) Client.reactApp = cast ReactDOM.render(jsx(''), js.Browser.document.getElementById("app")); 31 | 32 | // set the contents 33 | Client.reactApp.setState({content: data.content}); 34 | } 35 | } 36 | 37 | typedef RenderPageActionData = 38 | { 39 | content:Dynamic, 40 | } 41 | 42 | #else 43 | 44 | // on server-side it is just a plain view result 45 | abstract ReactResult(PartialViewResult) to PartialViewResult 46 | { 47 | public inline function new(_) 48 | this = new PartialViewResult({}, "../empty.html"); 49 | } 50 | 51 | #end -------------------------------------------------------------------------------- /integrated/02_reactjs/src/react/App.hx: -------------------------------------------------------------------------------- 1 | package react; 2 | 3 | import api.react.ReactMacro.jsx; 4 | import api.react.ReactComponent; 5 | import js.html.*; 6 | 7 | using ufront.MVC; 8 | using tink.CoreApi; 9 | 10 | class App extends ReactComponentOfState<{?content:ReactComponent}> 11 | { 12 | @inject 13 | public var ctx:HttpContext; 14 | 15 | public function new(props) 16 | { 17 | super(props); 18 | 19 | Client.injector.injectInto(this); // get our injections 20 | 21 | // initialize a state 22 | state = {} 23 | } 24 | 25 | override function render() 26 | { 27 | // just a proof-of-concept that you can really get the injected variables 28 | // basically you can inject anything here, e.g. remoting API, etc 29 | trace(ctx.auth.isLoggedIn()); 30 | 31 | return jsx(' 32 |
33 | {state.content} 34 |

35 | Note that when you click on the link above, the page is re-rendered by js (react),
36 | so that it is a single-page-app (SPA). But when you enter the URL directly
37 | (e.g. http://localhost:2987/about), you will still get the same result, because the
38 | the page is prepared on client-side (by ufrontApp.executeRequest() in Client.hx) 39 |

40 |
41 | '); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /minimal/01_static_results/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: Static Results 2 | 3 | In this example, we will set up a minimal server that will serve static results (e.g. static page, static json, static file, etc). -------------------------------------------------------------------------------- /minimal/01_static_results/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01_static_results", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/01_static_results/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/01_static_results/bin/view/home/async.html: -------------------------------------------------------------------------------- 1 |

2 | ::message:: 3 |

4 | (async.html) -------------------------------------------------------------------------------- /minimal/01_static_results/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 |

2 | ::message:: 3 |

-------------------------------------------------------------------------------- /minimal/01_static_results/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 |

11 | View Result
12 | Json Result
13 | Redirect Result
14 | File Result
15 | Async View Result
16 |

17 | ::viewContent:: 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /minimal/01_static_results/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /minimal/01_static_results/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Server 7 | { 8 | static function main() 9 | { 10 | var ufrontApp = new UfrontApplication({ 11 | indexController: HomeController, // all incoming requests will be handled by this controller 12 | defaultLayout: "layout.html", // use this file as the default layout for a ViewResult 13 | }); 14 | 15 | // listen for incoming connection at this port number 16 | ufrontApp.listen(2987); 17 | } 18 | } -------------------------------------------------------------------------------- /minimal/01_static_results/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import haxe.Timer; 4 | 5 | using ufront.MVC; 6 | using tink.CoreApi; 7 | 8 | class HomeController extends Controller 9 | { 10 | /** 11 | Serves a ViewResult, which reads a template file (usualy html) 12 | and fill in the variable values provided 13 | **/ 14 | @:route(GET, "/") 15 | public function main() 16 | { 17 | return new ViewResult({ 18 | title: "Ufront NodeJS Guide", 19 | message:"Welcome to Ufront!" 20 | }); 21 | } 22 | 23 | /** 24 | Serves a JsonResult, the Content-Type response header will be set to "application/json" 25 | **/ 26 | @:route(GET, "/json") 27 | public function json() 28 | { 29 | return new JsonResult({ 30 | title: "Ufront NodeJS Guide", 31 | message:"Welcome to Ufront!" 32 | }); 33 | } 34 | 35 | /** 36 | Serves a RedirectResult, which redirect the client to another path 37 | **/ 38 | @:route(GET, "/redirect") 39 | public function redirect() 40 | { 41 | return new RedirectResult("/"); 42 | } 43 | 44 | /** 45 | Serves a FilePathResult, which read a file at the path specified and return it. 46 | One can optionally specify the mime type and download file name. 47 | **/ 48 | @:route(GET, "/file") 49 | public function file() 50 | { 51 | return new FilePathResult("package.json"); 52 | } 53 | 54 | /** 55 | Advanced: 56 | The above examples return results in a sync manner. However, in many cases, 57 | (especially on nodejs) server cannot return immediately due to heavy tasks, 58 | network latency, etc. So, in order to return an async result, we can return a 59 | Future or Surprise of a result. To learn more about Future/Surprise, please 60 | read the doc of the tink_core library. 61 | **/ 62 | @:route(GET, "/async") 63 | public function async() 64 | { 65 | // simulate an async call 66 | var trigger = Future.trigger(); 67 | Timer.delay(trigger.trigger.bind("Welcome to Ufront! (Async)"), 2500); // delay 2.5 seconds 68 | 69 | // return the async result 70 | return trigger.asFuture() >> 71 | function(message:String) return new ViewResult({ 72 | title: "Ufront NodeJS Guide", 73 | message: message, 74 | }); 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /minimal/02_dynamic_results/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: Dynamic Results 2 | 3 | In this example, we will set up a minimal server that will handle GET/POST parameters 4 | 5 | ### Prerequisites 6 | 7 | - [Static Results](../01_static_results) -------------------------------------------------------------------------------- /minimal/02_dynamic_results/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "02_dynamic_results", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/02_dynamic_results/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/02_dynamic_results/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 |

GET

2 |
3 | 4 | 5 | 6 |
7 | 8 |

POST

9 |
10 | 11 | 12 | 13 |
14 | 15 |

PATH

16 | Go 17 | 18 |

MIXED

19 |
20 | 21 | 22 |
-------------------------------------------------------------------------------- /minimal/02_dynamic_results/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 | ::viewContent:: 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /minimal/02_dynamic_results/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /minimal/02_dynamic_results/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Server 7 | { 8 | static function main() 9 | { 10 | var ufrontApp = new UfrontApplication({ 11 | indexController: HomeController, // all incoming requests will be handled by this controller 12 | defaultLayout: "layout.html", // use this file as the default layout for a ViewResult 13 | }); 14 | 15 | // listen for incoming connection at this port number 16 | ufrontApp.listen(2987); 17 | } 18 | } -------------------------------------------------------------------------------- /minimal/02_dynamic_results/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | using ufront.MVC; 4 | 5 | class HomeController extends Controller 6 | { 7 | /** 8 | Main page 9 | **/ 10 | @:route(GET, "/") 11 | public function main() 12 | { 13 | return new ViewResult({ 14 | title: "Ufront NodeJS Guide", 15 | }); 16 | } 17 | 18 | /** 19 | GET parameters 20 | **/ 21 | @:route(GET, "/get") 22 | public function get(args:{param1:String, param2:Int}) 23 | { 24 | return new JsonResult({ 25 | param1: args.param1, 26 | param2: args.param2, 27 | }); 28 | } 29 | 30 | /** 31 | POST paramters 32 | **/ 33 | @:route(POST, "/post") 34 | public function post(args:{param1:String, param2:Int}) 35 | { 36 | return new JsonResult({ 37 | param1: args.param1, 38 | param2: args.param2, 39 | }); 40 | } 41 | 42 | /** 43 | You can also put the parameters as part of the url 44 | **/ 45 | @:route(GET, "/path/$param1/$param2") 46 | public function path(param1:String, param2:Int) 47 | { 48 | return new JsonResult({ 49 | param1: param1, 50 | param2: param2, 51 | }); 52 | } 53 | 54 | /** 55 | You can mix path paramters and query parameters 56 | **/ 57 | @:route(GET, "/mixed/$param1") 58 | public function mixed(param1:String, args:{param2:Int}) 59 | { 60 | return new JsonResult({ 61 | param1: param1, 62 | param2: args.param2, 63 | }); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /minimal/03_file_upload/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: File upload 2 | 3 | In this example, we will set up a minimal server that will handle uploaded files. 4 | 5 | ### Prerequisites 6 | 7 | - [Middleware](../09_middleware) -------------------------------------------------------------------------------- /minimal/03_file_upload/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "03_file_upload", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/03_file_upload/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/03_file_upload/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 |
-------------------------------------------------------------------------------- /minimal/03_file_upload/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 | ::viewContent:: 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /minimal/03_file_upload/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /minimal/03_file_upload/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Server 7 | { 8 | static function main() 9 | { 10 | // prepare the temp file upload middleware, which will save the uploaded 11 | // to a temp location, and delete it when the request ends 12 | var uploadMiddleware = new TmpFileUploadMiddleware(); 13 | 14 | var ufrontApp = new UfrontApplication({ 15 | indexController: HomeController, // all incoming requests will be handled by this controller 16 | defaultLayout: "layout.html", // use this file as the default layout for a ViewResult 17 | requestMiddleware: [uploadMiddleware], // handle the uploaded file when a request comes 18 | responseMiddleware: [uploadMiddleware], // make it a response middleware so that it will delete the temp file 19 | }); 20 | 21 | // listen for incoming connection at this port number 22 | ufrontApp.listen(2987); 23 | } 24 | } -------------------------------------------------------------------------------- /minimal/03_file_upload/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | using ufront.MVC; 4 | 5 | class HomeController extends Controller 6 | { 7 | /** 8 | Serves the upload form 9 | **/ 10 | @:route(GET, "/") 11 | public function main() 12 | { 13 | return new ViewResult({ 14 | title: "Ufront NodeJS Guide", 15 | }); 16 | } 17 | 18 | /** 19 | Handles the uploaded file. 20 | **/ 21 | @:route(POST, "/upload") 22 | public function upload() 23 | { 24 | var file = context.request.files['upload']; // use the form input name to retrieve the file 25 | if(file != null) 26 | { 27 | // save a copy of the file to disk, otherwise the temp uploaded file 28 | // will be deleted at the end of the request 29 | file.writeToFile(context.contentDirectory + file.originalFileName); 30 | } 31 | 32 | // return a result 33 | return new JsonResult({ 34 | success: file != null, 35 | }); 36 | } 37 | } -------------------------------------------------------------------------------- /minimal/04_client_app/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: Client Application 2 | 3 | In this example, we will set up a minimal client application. -------------------------------------------------------------------------------- /minimal/04_client_app/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "04_client_app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/04_client_app/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/04_client_app/bin/view/home/about.html: -------------------------------------------------------------------------------- 1 |

2 | About Page... 3 |

-------------------------------------------------------------------------------- /minimal/04_client_app/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 |

2 | ::message:: 3 |

-------------------------------------------------------------------------------- /minimal/04_client_app/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 |

11 | Try to click the following links and check your server log, there should be no request sent to server.
12 | Because the request is handled at client side, because of the "pushstate" class. 13 |

14 | 15 | Home 16 | About 17 | 18 |

19 | The following links will be handled by the server when the link has no "pushstate" class 20 |

21 | Home 22 | About 23 | 24 | 25 |

Page contents start here:

26 | ::viewContent:: 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /minimal/04_client_app/client.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # use the pushstate lib, which handles the browser history for us 5 | -lib pushstate 6 | 7 | # we are compiling for the client 8 | -D client 9 | 10 | # usual hxml things... 11 | -cp src 12 | -main Client 13 | -js bin/client.js -------------------------------------------------------------------------------- /minimal/04_client_app/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /minimal/04_client_app/src/Client.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Client 7 | { 8 | static function main() 9 | { 10 | var ufrontApp = new ClientJsApplication({ 11 | indexController: HomeController, 12 | defaultLayout: "layout.html", 13 | }); 14 | 15 | // Listen to any history changes using PushState, and process each request. 16 | ufrontApp.listen(); 17 | } 18 | } -------------------------------------------------------------------------------- /minimal/04_client_app/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Server 7 | { 8 | static function main() 9 | { 10 | var ufrontApp = new UfrontApplication({ 11 | indexController: HomeController, // all incoming requests will be handled by this controller 12 | defaultLayout: "layout.html", // use this file as the default layout for a ViewResult 13 | }); 14 | 15 | // listen for incoming connection at this port number 16 | ufrontApp.listen(2987); 17 | } 18 | } -------------------------------------------------------------------------------- /minimal/04_client_app/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | using ufront.MVC; 4 | 5 | class HomeController extends Controller 6 | { 7 | /** 8 | Serves a main page 9 | **/ 10 | @:route(GET, "/") 11 | public function main() 12 | { 13 | return new ViewResult({ 14 | title: "Ufront NodeJS Guide - Home", 15 | message:"Welcome to Ufront!" 16 | }); 17 | } 18 | 19 | /** 20 | Serves another page 21 | **/ 22 | @:route(GET, "/about") 23 | public function about() 24 | { 25 | return new ViewResult({ 26 | title: "Ufront NodeJS Guide - About", 27 | }); 28 | } 29 | } -------------------------------------------------------------------------------- /minimal/05_api/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: API 2 | 3 | In this example, we will set up a minimal server that will use an API 4 | 5 | In Ufront, we have controllers and API. Controllers mainly handles routes and parse parameters from a request, 6 | while API usually handle "core" logics, like interacting with database, and authentications. 7 | 8 | You can also put your "core" or authentications logic inside a controller, but that is HIGHLY DISCOURAGED. 9 | because controller code will be compiled into client app and you risk leaking 10 | critical information to users, also authentications done in client side is also not trustable. 11 | On the other hand, APIs are NOT compiled into client app, any API calls from client app is 12 | actually a remoting call, i.e. the API code are always run in the server instead of the client. 13 | This way your logics and secrets are protected, and we can make sure authentications are not maliciously manipulated. -------------------------------------------------------------------------------- /minimal/05_api/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "05_api", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/05_api/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/05_api/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 |

2 | ::message:: 3 |

-------------------------------------------------------------------------------- /minimal/05_api/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 | ::viewContent:: 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /minimal/05_api/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /minimal/05_api/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Server 7 | { 8 | static function main() 9 | { 10 | var ufrontApp = new UfrontApplication({ 11 | indexController: HomeController, // all incoming requests will be handled by this controller 12 | defaultLayout: "layout.html", // use this file as the default layout for a ViewResult 13 | }); 14 | 15 | // listen for incoming connection at this port number 16 | ufrontApp.listen(2987); 17 | } 18 | } -------------------------------------------------------------------------------- /minimal/05_api/src/api/AnotherApi.hx: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | using ufront.MVC; 4 | using tink.CoreApi; 5 | 6 | /** Just to demonstrate that we can reference another API **/ 7 | class AnotherApi extends UFApi 8 | { 9 | // inject the api we need 10 | @inject 11 | public var fileApi:FileApi; 12 | 13 | public function doSomething():Surprise 14 | { 15 | return fileApi.getFile('package.json'); 16 | } 17 | } -------------------------------------------------------------------------------- /minimal/05_api/src/api/FileApi.hx: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | import sys.io.File; 4 | import haxe.Timer; 5 | 6 | using ufront.MVC; 7 | using tink.CoreApi; 8 | 9 | class FileApi extends UFApi 10 | { 11 | public function getFile(path:String):Surprise 12 | { 13 | var content = File.getContent(path); 14 | 15 | // the file is read immediately, but we want to simulate an async call, 16 | // so we delay to return the result for 1sec (1000ms) 17 | // read docs of tink_core library to learn how Future/Surprise works 18 | var trigger = Future.trigger(); 19 | Timer.delay(function() trigger.trigger(Success(content)), 1000); 20 | return trigger; 21 | } 22 | } -------------------------------------------------------------------------------- /minimal/05_api/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import api.*; 4 | 5 | using ufront.MVC; 6 | 7 | class HomeController extends Controller 8 | { 9 | // inject our api (please read docs of the minject library) 10 | @inject 11 | public var fileApi:FileApi; 12 | 13 | @inject 14 | public var anotherApi:AnotherApi; 15 | 16 | /** 17 | Call the API and return the value 18 | **/ 19 | @:route(GET, "/") 20 | public function main() 21 | { 22 | return fileApi.getFile('package.json') >> 23 | function(content:String) return new ViewResult({ 24 | title: "Ufront NodeJS Guide", 25 | message: content, 26 | }); 27 | } 28 | 29 | /** 30 | For demonstration of API referring to another 31 | **/ 32 | @:route(GET, "/test") 33 | @template('main.html') // specify the template file, otherwise ufront by default will try to find 'test.html' 34 | public function test() 35 | { 36 | return anotherApi.doSomething() >> 37 | function(content:String) return new ViewResult({ 38 | title: "Ufront NodeJS Guide", 39 | message: content, 40 | }); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /minimal/06_basic_auth/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: Basic Authentication 2 | 3 | In this example, we will set up a minimal server that will have basic authentication. 4 | 5 | Note that "sessions" are not implemented in this minimal example, therefore the log-in status 6 | is only applicable to the current request. In other words, the log-in status will not persist across pages. 7 | 8 | ### Prerequisites 9 | 10 | - [Client App](../04_client_app) 11 | 12 | ## Overview 13 | 14 | The minimal code required for authentication mainly involves: 15 | - 2 interfaces: `UFAuthUser` & `UFAuthHandler` (see the `auth` package) 16 | - and the code to initialize them (see `AuthApi`) -------------------------------------------------------------------------------- /minimal/06_basic_auth/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "06_basic_auth", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/06_basic_auth/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/06_basic_auth/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 |

2 | Is Logged In? ::isLoggedIn:: 3 |

4 | 5 | 6 |

Log in here: (username: ufront, password: ufrontpw)

7 |
8 | 9 | 10 | 11 |
-------------------------------------------------------------------------------- /minimal/06_basic_auth/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 | ::viewContent:: 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /minimal/06_basic_auth/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /minimal/06_basic_auth/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import auth.Auth; 4 | import controller.HomeController; 5 | using ufront.MVC; 6 | 7 | class Server 8 | { 9 | static function main() 10 | { 11 | var ufrontApp = new UfrontApplication({ 12 | indexController: HomeController, 13 | defaultLayout: "layout.html", 14 | authImplementation: Auth, // specify our auth implementation 15 | }); 16 | 17 | // listen for incoming connection at this port number 18 | ufrontApp.listen(2987); 19 | } 20 | } -------------------------------------------------------------------------------- /minimal/06_basic_auth/src/api/AuthApi.hx: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | import auth.Auth; 4 | 5 | using ufront.MVC; 6 | using tink.CoreApi; 7 | 8 | class AuthApi extends UFApi 9 | { 10 | public function login(username:String, password:String):Surprise 11 | { 12 | // check the username and password, 13 | // (note: in reality you should probably check against the database instead of hardcoding like this) 14 | if(username == 'ufront' && password == 'ufrontpw') 15 | { 16 | // cast the auth interface into our implementation 17 | var authImplementation = Std.instance(auth, Auth); 18 | 19 | // initialize our auth handler 20 | authImplementation.init(username); 21 | } 22 | 23 | // our simple authentication logic example here is a sync operation. 24 | // in reality, you may want to query the database or do other async operations, 25 | // in that case you can return a "real" future instead of a "sync future" 26 | // as what I am doing here. 27 | return Future.sync(Success(Noise)); 28 | } 29 | } -------------------------------------------------------------------------------- /minimal/06_basic_auth/src/auth/Auth.hx: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | using ufront.MVC; 4 | 5 | class Auth implements UFAuthHandler 6 | { 7 | // required by the interface 8 | public var currentUser(get, never):UFAuthUser; 9 | 10 | // actually stores the user; 11 | var _currentUser:User; 12 | 13 | public function new() 14 | { 15 | } 16 | 17 | // initialize our auth handler with an user id, 18 | // this user id should be the result of some authentication (e.g. username/password) 19 | public function init(userId:String) 20 | { 21 | _currentUser = new User(userId); 22 | } 23 | 24 | // required by the interface 25 | public function hasPermission (permission:EnumValue):Bool 26 | { 27 | return isLoggedIn() && _currentUser.can(permission); 28 | } 29 | 30 | // required by the interface 31 | public function hasPermissions (permissions:Iterable):Bool 32 | { 33 | return isLoggedIn() && _currentUser.can(permissions); 34 | } 35 | 36 | // required by the interface 37 | public function isLoggedIn():Bool 38 | { 39 | return _currentUser != null; 40 | } 41 | 42 | // required by the interface 43 | public function isLoggedInAs(user:UFAuthUser):Bool 44 | { 45 | return isLoggedIn() && _currentUser.userID == user.userID; 46 | } 47 | 48 | // required by the interface 49 | public function requireLogin ():Void 50 | { 51 | if(!isLoggedIn()) throw HttpError.authError(ANotLoggedIn); 52 | } 53 | 54 | // required by the interface 55 | public function requireLoginAs (user:UFAuthUser):Void 56 | { 57 | if(!isLoggedInAs(user)) throw HttpError.authError(ANotLoggedInAs(user)); 58 | } 59 | 60 | // required by the interface 61 | public function requirePermission (permission:EnumValue):Void 62 | { 63 | if(!hasPermission(permission)) throw HttpError.authError(ANoPermission(permission)); 64 | } 65 | 66 | // required by the interface 67 | public function requirePermissions (permissions:Iterable):Void 68 | { 69 | for(permission in permissions) if(!hasPermission(permission)) throw HttpError.authError(ANoPermission(permission)); 70 | } 71 | 72 | // required by the interface 73 | public function toString ():String 74 | { 75 | return "Auth"; 76 | } 77 | 78 | function get_currentUser() return _currentUser; 79 | } 80 | -------------------------------------------------------------------------------- /minimal/06_basic_auth/src/auth/User.hx: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | using ufront.MVC; 4 | 5 | class User implements UFAuthUser 6 | { 7 | // required by the interface 8 | public var userID(get, never):String; 9 | 10 | // actually stores the user id 11 | var userId:String; 12 | 13 | public function new(userId:String) 14 | { 15 | this.userId = userId; 16 | } 17 | 18 | // required by the interface 19 | public function can(?permission:EnumValue, ?permissions:Iterable):Bool 20 | { 21 | // for minimal demonstration purpose, here simply returns true to allow all permissions. 22 | // in reality, you should implement your own logic (e.g. query database, etc) 23 | // to determine if an user is "can" do the specified permissions 24 | return true; 25 | } 26 | 27 | function get_userID() return userId; 28 | } -------------------------------------------------------------------------------- /minimal/06_basic_auth/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import api.*; 4 | 5 | using ufront.MVC; 6 | 7 | class HomeController extends Controller 8 | { 9 | @inject 10 | public var authApi:AuthApi; 11 | 12 | @:route(GET, "/") 13 | public function main(?args:{username:String, password:String}) 14 | { 15 | // try to login with the provided credentials 16 | // there is no problem if they are not provided, 17 | // the api will simply skip the process. 18 | return authApi.login(args.username, args.password) >> 19 | function(_) 20 | { 21 | // we can access the auth handler from `context.auth` 22 | var isLoggedIn = context.auth.isLoggedIn(); 23 | 24 | return new ViewResult({ 25 | title: "Ufront NodeJS Guide", 26 | isLoggedIn: isLoggedIn, 27 | }); 28 | } 29 | 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /minimal/07_remoting/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: Remoting 2 | 3 | In this example, we will set up a minimal client application that use remoting. 4 | 5 | Remoting is the type-safe way for a client to communicate with the server. 6 | Some quick comparison of Haxe remoting vs AJAX: 7 | 8 | 0. Haxe remoting is fully typed vs ajax is just plain text or json 9 | 0. We can basically send ANY haxe types over the wire with haxe remoting, including enum, class instances, etc. 10 | 0. Since it is fully typed, it gives you autocomplete in IDE 11 | 0. It helps you share the same piece of code between server and client 12 | 0. It is cross-platform: same code, compile to different targets 13 | 14 | ### Prerequisites 15 | 16 | - [API](../05_api) 17 | -------------------------------------------------------------------------------- /minimal/07_remoting/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "07_remoting", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/07_remoting/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/07_remoting/bin/view/home/about.html: -------------------------------------------------------------------------------- 1 |

2 | About Page... 3 |

-------------------------------------------------------------------------------- /minimal/07_remoting/bin/view/home/main.html: -------------------------------------------------------------------------------- 1 |

2 | ::message:: 3 |

4 |

5 | Rendered by: ::renderedBy:: 6 |

7 | 8 | 9 |

Try to input something and click submit, the "GET" request will not be sent to server, 10 |
because it is handled by pushstate. Instead, the client will handle the request and, 11 |
for the API call it will use remoting so that the API call is actually run on the server. 12 |
You can check the server output to verify that.

13 | 14 |
15 | 16 | 17 |
-------------------------------------------------------------------------------- /minimal/07_remoting/bin/view/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ::title:: 7 | 8 | 9 | 10 | 11 | ::viewContent:: 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /minimal/07_remoting/client.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # use the pushstate lib, which handles the browser history for us 5 | -lib pushstate 6 | 7 | # we are compiling for the client 8 | -D client 9 | 10 | # usual hxml things... 11 | -cp src 12 | -main Client 13 | -js bin/client.js -------------------------------------------------------------------------------- /minimal/07_remoting/server.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # we need hxnodejs because we are targeting nodejs 5 | -lib hxnodejs 6 | 7 | # ufront uses expressjs behind the hood 8 | -lib express 9 | 10 | # we are compiling for the server 11 | -D server 12 | 13 | # usual hxml things... 14 | -cp src 15 | -main Server 16 | -js bin/server.js -------------------------------------------------------------------------------- /minimal/07_remoting/src/Client.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import controller.HomeController; 4 | using ufront.MVC; 5 | 6 | class Client 7 | { 8 | static function main() 9 | { 10 | var ufrontApp = new ClientJsApplication({ 11 | indexController: HomeController, 12 | defaultLayout: "layout.html", 13 | }); 14 | 15 | // Listen to any history changes using PushState, and process each request. 16 | ufrontApp.listen(); 17 | } 18 | } -------------------------------------------------------------------------------- /minimal/07_remoting/src/Server.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import api.ApiContext; 4 | import controller.HomeController; 5 | using ufront.MVC; 6 | 7 | class Server 8 | { 9 | static function main() 10 | { 11 | var ufrontApp = new UfrontApplication({ 12 | indexController: HomeController, 13 | remotingApi: ApiContext, // specify our remoting API context (interface) 14 | defaultLayout: "layout.html", 15 | }); 16 | 17 | // listen for incoming connection at this port number 18 | ufrontApp.listen(2987); 19 | } 20 | } -------------------------------------------------------------------------------- /minimal/07_remoting/src/api/ApiContext.hx: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | import api.TestApi; 4 | using ufront.MVC; 5 | 6 | class ApiContext extends UFApiContext 7 | { 8 | public var testApi:TestApi; 9 | } -------------------------------------------------------------------------------- /minimal/07_remoting/src/api/TestApi.hx: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | using ufront.MVC; 4 | using tink.CoreApi; 5 | 6 | class TestApi extends UFApi 7 | { 8 | public function test(param:String):Surprise 9 | { 10 | // make sure this piece of code is compiled in the server only 11 | // to demonstrate that the code is actually run in the server, 12 | // even when the API is called form client 13 | #if server 14 | param = '$param (server)'; 15 | #end 16 | 17 | // just shorthand to `Future.sync(Success(param))` 18 | return param.asGoodSurprise(); 19 | } 20 | } 21 | 22 | 23 | // UFAsyncApi will do the magic, converting all the api calls to remoting calls for the client 24 | class AsyncTestApi extends UFAsyncApi {} -------------------------------------------------------------------------------- /minimal/07_remoting/src/controller/HomeController.hx: -------------------------------------------------------------------------------- 1 | package controller; 2 | 3 | import api.TestApi; 4 | 5 | using ufront.MVC; 6 | 7 | class HomeController extends Controller 8 | { 9 | @inject 10 | public var testApi:AsyncTestApi; 11 | 12 | @:route(GET, "/") 13 | public function main(?args:{param:String}) 14 | { 15 | return testApi.test(args.param) >> 16 | function(result:String) return new ViewResult({ 17 | title: "Ufront NodeJS Guide - Home", 18 | message: 'Result: $result', // print the result of the API call 19 | renderedBy: #if server 'Server' #else 'Client' #end, // let us know if this page is rendered by client or server 20 | }); 21 | } 22 | } -------------------------------------------------------------------------------- /minimal/08_tasks/README.md: -------------------------------------------------------------------------------- 1 | # Minimal example: Ufront Tasks 2 | 3 | In this example, we will set up a minimal task script. 4 | 5 | Ufront task scripts are program that run in the command line, acting like a hybrid of 6 | server and client. In fact, we do not actually need a server here because all the API functions 7 | are compiled into the task script, making it like a server that doesn't listen to incoming 8 | http request, but instead invoked through command line. 9 | 10 | Since the task script resides in the server, you can write admin programs in this 11 | way. For example, you can write a task that will clear obsolete/expired data from the database, 12 | or a task that will call an API to send out emails. 13 | 14 | UFTaskSet is based on the [mcli](https://github.com/waneck/mcli) library, for more usage details please refer to mcli docs. 15 | 16 | ## How to build / run 17 | 18 | #### Build: 19 | 20 | ```bash 21 | haxe tasks.hxml 22 | ``` 23 | 24 | 25 | #### Run: 26 | 27 | ```bash 28 | cd bin 29 | node tasks.js --test 30 | node tasks.js --read-path start_server.sh 31 | ``` -------------------------------------------------------------------------------- /minimal/08_tasks/bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "08_tasks", 3 | "version": "1.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "compression": "^1.6.1", 7 | "body-parser": "^1.15.0", 8 | "cookie-parser": "^1.4.1", 9 | "express": "^4.13.4", 10 | "multer": "^1.1.0" 11 | }, 12 | "scripts": { 13 | "start": "./start_server.sh" 14 | } 15 | } -------------------------------------------------------------------------------- /minimal/08_tasks/bin/start_server.sh: -------------------------------------------------------------------------------- 1 | nodemon server.js -------------------------------------------------------------------------------- /minimal/08_tasks/src/Tasks.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import api.*; 4 | import ufront.tasks.UFTaskSet; 5 | 6 | using ufront.MVC; 7 | using tink.CoreApi; 8 | 9 | class Tasks extends UFTaskSet 10 | { 11 | static function main() 12 | { 13 | var tasks = new Tasks(); 14 | 15 | // Inject our content directory in case we need to write to it. 16 | tasks.injector.map(String, "contentDirectory").toValue("../uf-content/"); 17 | 18 | // Inject the auth system if you need auth in your APIs. 19 | // tasks.injector.map(UFAuthHandler).toClass(YesBossAuthAHandler); 20 | 21 | // Inject all our APIs. 22 | for (api in CompileTime.getAllClasses(UFApi)) tasks.injector.mapRuntimeTypeOf(api).toSingleton(api); 23 | 24 | // Use CLI logging, which prints both to a log file and to the CLI output. 25 | tasks.useCLILogging("logs/tasks.log"); 26 | 27 | // Execute the task runner 28 | var args = Sys.args(); 29 | tasks.execute(args); 30 | 31 | } 32 | 33 | // inject our API 34 | @inject @:skip 35 | public var fileApi:FileApi; 36 | 37 | /** 38 | A demonstration to show that you can call an API from a task script. 39 | 40 | Usage: node tasks.js --test 41 | **/ 42 | public function test() 43 | { 44 | // You can call any API from a task script 45 | fileApi.getFile('package.json').handle( 46 | function(content) 47 | { 48 | trace(content.sure()); 49 | Sys.exit(0); 50 | } 51 | ); 52 | } 53 | 54 | /** 55 | A demonstration to show that you can pass parameters to task script. 56 | (Refer to mcli docs for more details.) 57 | 58 | Usage: node tasks.js --read-path package.json 59 | **/ 60 | public function readPath(path:String) 61 | { 62 | // You can call any API from a task script 63 | fileApi.getFile(path).handle( 64 | function(content) 65 | { 66 | trace(content.sure()); 67 | Sys.exit(0); 68 | } 69 | ); 70 | } 71 | } -------------------------------------------------------------------------------- /minimal/08_tasks/src/api/FileApi.hx: -------------------------------------------------------------------------------- 1 | package api; 2 | 3 | import sys.io.File; 4 | import haxe.Timer; 5 | 6 | using ufront.MVC; 7 | using tink.CoreApi; 8 | 9 | class FileApi extends UFApi 10 | { 11 | public function getFile(path:String):Surprise 12 | { 13 | var content = File.getContent(path); 14 | 15 | // the file is read immediately, but we want to simulate an async call, 16 | // so we delay to return the result for 1sec (1000ms) 17 | // read docs of tink_core library to learn how Future/Surprise works 18 | var trigger = Future.trigger(); 19 | Timer.delay(function() trigger.trigger(Success(content)), 1000); 20 | return trigger; 21 | } 22 | } -------------------------------------------------------------------------------- /minimal/08_tasks/tasks.hxml: -------------------------------------------------------------------------------- 1 | # use the ufront-mvc lib 2 | -lib ufront-mvc 3 | 4 | # the uftasks library 5 | -lib ufront-uftasks 6 | 7 | # we need hxnodejs because we are targeting nodejs 8 | -lib hxnodejs 9 | 10 | # ufront uses expressjs behind the hood 11 | -lib express 12 | 13 | # we are compiling for the server 14 | -D server 15 | 16 | # usual hxml things... 17 | -cp src 18 | -main Tasks 19 | -js bin/tasks.js -------------------------------------------------------------------------------- /minimal/09_middleware/.wip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinresol/ufront-nodejs-guide/a4b92bfd7d8257388ab698fa90af4643dbb8ef7b/minimal/09_middleware/.wip --------------------------------------------------------------------------------