copied from a project that was version on mercurial
This commit is contained in:
commit
ec72b5b91a
24 files changed
+841
No files matched your search
@@ -0,0 +1,5 @@
|
||||
js/
|
||||
bower_components/
|
||||
node_modules/
|
||||
lib/
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
module.exports = (grunt) ->
|
||||
|
||||
# Project configuration.
|
||||
grunt.initConfig(
|
||||
packagekg: grunt.file.readJSON('package.json')
|
||||
bower:
|
||||
install: {}
|
||||
|
||||
connect:
|
||||
server:
|
||||
options:
|
||||
port: 4506
|
||||
base: '.'
|
||||
keepalive: true
|
||||
|
||||
clean: ["js"]
|
||||
|
||||
coffee:
|
||||
compile:
|
||||
expand: true
|
||||
cwd: 'coffee'
|
||||
src: ['**/*.coffee']
|
||||
dest: 'js'
|
||||
ext: '.js'
|
||||
|
||||
watch:
|
||||
coffee:
|
||||
files: ['**/*.coffee']
|
||||
tasks: ['coffee']
|
||||
options:
|
||||
spawn: false
|
||||
|
||||
concurrent:
|
||||
target: ['watch', 'connect'],
|
||||
|
||||
)
|
||||
|
||||
# Plugins
|
||||
grunt.loadNpmTasks('grunt-bower-task')
|
||||
grunt.loadNpmTasks('grunt-contrib-connect')
|
||||
grunt.loadNpmTasks('grunt-contrib-coffee')
|
||||
grunt.loadNpmTasks('grunt-contrib-watch')
|
||||
grunt.loadNpmTasks('grunt-concurrent')
|
||||
grunt.loadNpmTasks('grunt-contrib-clean')
|
||||
|
||||
# tasks
|
||||
grunt.registerTask('default', ['bower:install', 'clean', 'coffee:compile'])
|
||||
grunt.registerTask('dev', ['default', 'concurrent:target'])
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "bab",
|
||||
"version": "0.1",
|
||||
"authors": [
|
||||
"didier.amyot <didier.amyot@gmail.com>"
|
||||
],
|
||||
"description": "Easy home to all your web application.",
|
||||
"main": "index.html",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"underscore": "~1.6.0",
|
||||
"foundation": "~5.2.1",
|
||||
"angular": "~1.2.14",
|
||||
"angular-route": "~1.2.14",
|
||||
"angular-local-storage": "~0.0.1",
|
||||
"qunit": "~1.14.0",
|
||||
"zepto": "~1.1.3",
|
||||
"angular-sanitize": "~1.2.16",
|
||||
"fontawesome": "~4.0.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
root = this;
|
||||
|
||||
angular.module('bab', [
|
||||
'ngRoute'
|
||||
'bab.filters'
|
||||
'bab.services'
|
||||
'bab.directives'
|
||||
'bab.controllers'
|
||||
]).
|
||||
config(['$routeProvider', ($routeProvider) ->
|
||||
$routeProvider.when('/', {templateUrl: 'partials/home.html', controller: 'homeCtrl'})
|
||||
$routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'loginCtrl'})
|
||||
$routeProvider.when('/about', {templateUrl: 'partials/about.html', controller: 'aboutCtrl'})
|
||||
$routeProvider.otherwise({redirectTo: '/'})
|
||||
])
|
||||
|
||||
root.filters = angular.module('bab.filters', [])
|
||||
root.services = angular.module('bab.services', [])
|
||||
root.directives = angular.module('bab.directives', [])
|
||||
root.controllers = angular.module('bab.controllers', [])
|
||||
|
||||
|
||||
root.filters.filter 'month', () ->
|
||||
(text) ->
|
||||
return ['janvier',
|
||||
'février',
|
||||
'mars',
|
||||
'avril',
|
||||
'may',
|
||||
'juin',
|
||||
'juillet',
|
||||
'aout',
|
||||
'september',
|
||||
'octobre',
|
||||
'novembre',
|
||||
'decembre'][Number(text) - 1]
|
||||
@@ -0,0 +1,4 @@
|
||||
root = this;
|
||||
|
||||
root.controllers.controller('aboutCtrl', ['$scope', ($scope) ->
|
||||
])
|
||||
@@ -0,0 +1,8 @@
|
||||
root = this;
|
||||
|
||||
|
||||
root.controllers.controller('homeCtrl', ['$scope', '$http', ($scope, $http) ->
|
||||
$scope.services = []
|
||||
$http.get('/webapps').then (resp) ->
|
||||
$scope.services = resp.data
|
||||
])
|
||||
@@ -0,0 +1,4 @@
|
||||
root = this;
|
||||
|
||||
root.controllers.controller('loginCtrl', ['$scope', ($scope) ->
|
||||
])
|
||||
@@ -0,0 +1,9 @@
|
||||
root = this;
|
||||
|
||||
root.controllers.controller('topBarCtrl', ['$scope', ($scope) ->
|
||||
$scope.$on('$routeChangeSuccess', (next, current) ->
|
||||
$scope.path = current.originalPath
|
||||
)
|
||||
|
||||
$scope.path = "home"
|
||||
])
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
template = """
|
||||
<div class="panel">
|
||||
<div class="row">
|
||||
<div class="large-12 columns" style="text-align:center;">
|
||||
<a href="{{ service.url }}"><img src="{{ service.image }}" style="height:100px;"></img></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns" style="text-align:center;">
|
||||
<h3 ><a href="{{ service.url }}"> {{ service.name }} </a></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
directive = ($rootScope) ->
|
||||
directive =
|
||||
template: template
|
||||
replace: true,
|
||||
transclude: false,
|
||||
restrict: 'E',
|
||||
scope:
|
||||
service: "="
|
||||
controller:["$scope",
|
||||
($scope, $timeout) ->
|
||||
|
||||
]
|
||||
return directive
|
||||
|
||||
directives.directive("babService", ['$rootScope', directive])
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" width="401.98px" height="559.472px" viewBox="0 0 401.98 559.472" enable-background="new 0 0 401.98 559.472"
|
||||
xml:space="preserve">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#F6D2A2" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M10.634,300.493c0.764,15.751,16.499,8.463,23.626,3.539c6.765-4.675,8.743-0.789,9.337-10.015
|
||||
c0.389-6.064,1.088-12.128,0.744-18.216c-10.23-0.927-21.357,1.509-29.744,7.602C10.277,286.542,2.177,296.561,10.634,300.493"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#C6B198" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M10.634,300.493c2.29-0.852,4.717-1.457,6.271-3.528"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#6AD7E5" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M46.997,112.853C-13.3,95.897,31.536,19.189,79.956,50.74L46.997,112.853z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#6AD7E5" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M314.895,44.984c47.727-33.523,90.856,42.111,35.388,61.141L314.895,44.984z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#F6D2A2" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M325.161,494.343c12.123,7.501,34.282,30.182,16.096,41.18c-17.474,15.999-27.254-17.561-42.591-22.211
|
||||
C305.271,504.342,313.643,496.163,325.161,494.343z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M341.257,535.522c-2.696-5.361-3.601-11.618-8.102-15.939"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#F6D2A2" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M108.579,519.975c-14.229,2.202-22.238,15.039-34.1,21.558c-11.178,6.665-15.454-2.134-16.461-3.92
|
||||
c-1.752-0.799-1.605,0.744-4.309-1.979c-10.362-16.354,10.797-28.308,21.815-36.432C90.87,496.1,100.487,509.404,108.579,519.975z"
|
||||
/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M58.019,537.612c0.542-6.233,5.484-10.407,7.838-15.677"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.513,91.667c-7.955-4.208-13.791-9.923-8.925-19.124
|
||||
c4.505-8.518,12.874-7.593,20.83-3.385L49.513,91.667z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M337.716,83.667c7.955-4.208,13.791-9.923,8.925-19.124
|
||||
c-4.505-8.518-12.874-7.593-20.83-3.385L337.716,83.667z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#F6D2A2" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M392.475,298.493c-0.764,15.751-16.499,8.463-23.626,3.539c-6.765-4.675-8.743-0.789-9.337-10.015
|
||||
c-0.389-6.064-1.088-12.128-0.744-18.216c10.23-0.927,21.357,1.509,29.744,7.602C392.831,284.542,400.932,294.561,392.475,298.493"
|
||||
/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#C6B198" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M392.475,298.493c-2.29-0.852-4.717-1.457-6.271-3.528"/>
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#6AD7E5" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M195.512,13.124c60.365,0,116.953,8.633,146.452,66.629c26.478,65.006,17.062,135.104,21.1,203.806
|
||||
c3.468,58.992,11.157,127.145-16.21,181.812c-28.79,57.514-100.73,71.982-160,69.863c-46.555-1.666-102.794-16.854-129.069-59.389
|
||||
c-30.826-49.9-16.232-124.098-13.993-179.622c2.652-65.771-17.815-131.742,3.792-196.101
|
||||
C69.999,33.359,130.451,18.271,195.512,13.124"/>
|
||||
</g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#000000" stroke-width="2.9081" stroke-linecap="round" d="
|
||||
M206.169,94.16c10.838,63.003,113.822,46.345,99.03-17.197C291.935,19.983,202.567,35.755,206.169,94.16"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#000000" stroke-width="2.8214" stroke-linecap="round" d="
|
||||
M83.103,104.35c14.047,54.85,101.864,40.807,98.554-14.213C177.691,24.242,69.673,36.957,83.103,104.35"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M218.594,169.762c0.046,8.191,1.861,17.387,0.312,26.101c-2.091,3.952-6.193,4.37-9.729,5.967c-4.89-0.767-9.002-3.978-10.963-8.552
|
||||
c-1.255-9.946,0.468-19.576,0.785-29.526L218.594,169.762z"/>
|
||||
<g>
|
||||
<ellipse fill-rule="evenodd" clip-rule="evenodd" cx="107.324" cy="95.404" rx="14.829" ry="16.062"/>
|
||||
<ellipse fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" cx="114.069" cy="99.029" rx="3.496" ry="4.082"/>
|
||||
</g>
|
||||
<g>
|
||||
<ellipse fill-rule="evenodd" clip-rule="evenodd" cx="231.571" cy="91.404" rx="14.582" ry="16.062"/>
|
||||
<ellipse fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" cx="238.204" cy="95.029" rx="3.438" ry="4.082"/>
|
||||
</g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||
M176.217,168.87c-6.47,15.68,3.608,47.035,21.163,23.908c-1.255-9.946,0.468-19.576,0.785-29.526L176.217,168.87z"/>
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#F6D2A2" stroke="#231F20" stroke-width="3" stroke-linecap="round" d="
|
||||
M178.431,138.673c-12.059,1.028-21.916,15.366-15.646,26.709c8.303,15.024,26.836-1.329,38.379,0.203
|
||||
c13.285,0.272,24.17,14.047,34.84,2.49c11.867-12.854-5.109-25.373-18.377-30.97L178.431,138.673z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M176.913,138.045c-0.893-20.891,38.938-23.503,43.642-6.016
|
||||
C225.247,149.475,178.874,153.527,176.913,138.045C175.348,125.682,176.913,138.045,176.913,138.045z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.6 KiB |
Executable
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?><svg width="629" height="999" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<path id="svg_1" fill="#ffffff" d="m627,993l1,-988l-627,0l0,986l626,2z"/>
|
||||
<ellipse id="svg_2" fill="#000000" ry="12" rx="189" cy="898" cx="320"/>
|
||||
<ellipse id="svg_3" fill="#000000" ry="411" rx="12" cy="494" cx="543"/>
|
||||
<ellipse id="svg_4" fill="#000000" ry="11" rx="191" cy="94" cx="320"/>
|
||||
<ellipse id="svg_5" fill="#000000" ry="412" rx="12" cy="496" cx="97"/>
|
||||
<ellipse id="svg_6" fill="#000000" ry="156" rx="11" cy="702" cx="155"/>
|
||||
<ellipse id="svg_7" fill="#000000" ry="12" rx="132" cy="841" cx="316"/>
|
||||
<ellipse id="svg_8" fill="#000000" ry="152" rx="12" cy="706" cx="478"/>
|
||||
<ellipse id="svg_9" fill="#000000" ry="11" rx="131" cy="565" cx="318"/>
|
||||
<ellipse id="svg_10" fill="#000000" ry="154" rx="11" cy="297" cx="155"/>
|
||||
<ellipse id="svg_11" fill="#000000" ry="11" rx="126" cy="435" cx="316"/>
|
||||
<path id="svg_12" fill="#000000" d="m487,437l1,-279l-1,-5l-2,-4l-4,-2l-5,-1l-4,1l-4,2l-2,4l-1,4l-1,280l1,5l2,4l4,2l4,1l5,-1l4,-2l2,-4l1,-5z"/>
|
||||
<ellipse id="svg_13" fill="#000000" ry="11" rx="129" cy="157" cx="315"/>
|
||||
<circle id="svg_14" fill="#fff" r="8" cy="493" cx="478"/>
|
||||
<circle id="svg_15" fill="#000000" r="31" cy="493" cx="478"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="en" ng-app="bab">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>bab</title>
|
||||
|
||||
<!-- If you are using CSS version, only link these 2 files, you may add app.css to use for your overrides if you like. -->
|
||||
<link rel="stylesheet" href="bower_components/foundation/css/foundation.css">
|
||||
<link rel="stylesheet" href="bower_components/foundation/css/normalize.css">
|
||||
<link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.css">
|
||||
|
||||
|
||||
<script src="bower_components/angular/angular.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h3><img src="images/logo.svg" width="50px"> Bab</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-controller="topBarCtrl">
|
||||
<div class="large-12 columns">
|
||||
<dl class="sub-nav">
|
||||
<dd ng-class="{active: path == '/'}"><a href="#/">Web Apps</a></dd>
|
||||
<dd ng-class="{active: path == '/about'}"><a href="#/about">About</a></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-view></div>
|
||||
|
||||
<script src="bower_components/underscore/underscore.js"></script>
|
||||
<script src="bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="bower_components/foundation/js/foundation.js"></script>
|
||||
<script src="bower_components/angular-route/angular-route.js"></script>
|
||||
|
||||
<script src="js/babApp.js"></script>
|
||||
<script src="js/controllers/homeCtrl.js"></script>
|
||||
<script src="js/controllers/loginCtrl.js"></script>
|
||||
<script src="js/controllers/aboutCtrl.js"></script>
|
||||
<script src="js/controllers/topBarCtrl.js"></script>
|
||||
<script src="js/directives/service.js"></script>
|
||||
|
||||
|
||||
<script>
|
||||
$(document).foundation();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.4",
|
||||
"grunt-bower-task": "^0.3.4",
|
||||
"grunt-contrib-connect": "^0.7.1",
|
||||
"grunt-contrib-coffee": "^0.10.1",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-concurrent": "^0.5.0",
|
||||
"grunt-contrib-clean": "^0.5.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<div class="row">
|
||||
<div class="columns large-12">
|
||||
<p>
|
||||
Thanks for using <it>Bab</it>.
|
||||
</p>
|
||||
<p>
|
||||
The source code of the application is available on
|
||||
<a href="https://bitbucket.org/damyot/bab">bitbucket</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 100px;">
|
||||
<div class="large-4 large-centered columns" style="text-align: center;">
|
||||
<img src="images/gopher.svg" width="100px">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row" >
|
||||
<div class="large-4 large-centered columns" style="text-align: center;">
|
||||
<p>This program has been done with <a href="http://golang.org">Go</a>!</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<ul class="small-block-grid-2 medium-block-grid-3 large-block-grid-4">
|
||||
<li ng-repeat="service in services">
|
||||
<bab-service service=service></bab-service>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="row">
|
||||
<div class="columns large-6 large-offset-6">
|
||||
<form>
|
||||
<fieldset>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<label>email
|
||||
<input type="text">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<label>password
|
||||
<input type="password">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<button class="button">login</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"io/ioutil"
|
||||
"launchpad.net/goyaml"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
const helloworld string = `
|
||||
____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____
|
||||
||s ||||e ||||r ||||v ||||i ||||n ||||g ||||: ||||8 ||||0 ||||0 ||||0 ||
|
||||
||__||||__||||__||||__||||__||||__||||__||||__||||__||||__||||__||||__||
|
||||
|/__\||/__\||/__\||/__\||/__\||/__\||/__\||/__\||/__\||/__\||/__\||/__\|
|
||||
|
||||
`
|
||||
|
||||
type WebAppServers []*WebAppServer
|
||||
|
||||
func (this WebAppServers) Handle(w http.ResponseWriter, request *http.Request) {
|
||||
for _, was := range this {
|
||||
if was.Subdomain == mux.Vars(request)["subdomain"] {
|
||||
was.Handle(w, request)
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, "could not find a web application for ")
|
||||
}
|
||||
|
||||
func (this WebAppServers) Localize(domain string) WebAppServers {
|
||||
fmt.Println(">>", domain)
|
||||
ret := make(WebAppServers, len(this))
|
||||
for i, was := range this {
|
||||
ret[i] = was.Localize(domain)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var webAppServers WebAppServers
|
||||
var port int = 8000
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
webAppServers, err = loadWebAppServers()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadWebAppServers() (WebAppServers, error) {
|
||||
data, err := ioutil.ReadFile("webapps.yml")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loadWebAppServersFromYamlFile(data)
|
||||
|
||||
}
|
||||
|
||||
func loadWebAppServersFromYamlFile(yamlFile []byte) (WebAppServers, error) {
|
||||
ret := make(WebAppServers, 0, 10)
|
||||
err := goyaml.Unmarshal(yamlFile, &ret)
|
||||
return ret, err
|
||||
|
||||
}
|
||||
|
||||
func serveStatic(router *mux.Router) {
|
||||
handler := func(w http.ResponseWriter, request *http.Request) {
|
||||
vars := mux.Vars(request)
|
||||
filepath := "/" + vars["path"]
|
||||
w.Header().Set("Cache-Control", "public, max-age=43200")
|
||||
http.ServeFile(w, request, path.Join("client/", filepath))
|
||||
}
|
||||
router.HandleFunc("/{path:.*}", handler)
|
||||
}
|
||||
|
||||
func ServeWebApps(router *mux.Router) {
|
||||
router.HandleFunc("/{path:.*}", webAppServers.Handle)
|
||||
}
|
||||
|
||||
func WebappHandler(w http.ResponseWriter, request *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
encoder := json.NewEncoder(w)
|
||||
encoder.Encode(webAppServers.Localize(request.Host))
|
||||
}
|
||||
|
||||
func createMuxRouter() http.Handler {
|
||||
r := mux.NewRouter()
|
||||
ServeWebApps(r.Host(`{subdomain:\w+}.{domain:\w+}.{topleveldomain:[a-z]+}`).Subrouter())
|
||||
ServeWebApps(r.Host(`{subdomain:\w+}.localhost`).Subrouter())
|
||||
ServeWebApps(r.Host(`{subdomain:\w+}.\d{1-3}.\d{1-3}.\d{1-3}.\d{1-3}`).Subrouter())
|
||||
r.HandleFunc("/webapps", WebappHandler)
|
||||
serveStatic(r.PathPrefix("/").Subrouter())
|
||||
return r
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Print(helloworld)
|
||||
address := fmt.Sprintf("0.0.0.0:%v", port)
|
||||
if err := http.ListenAndServe(address, createMuxRouter()); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadWebAppServersFromYamlFile(t *testing.T) {
|
||||
yml := []byte(`
|
||||
---
|
||||
|
||||
-
|
||||
name: mongs
|
||||
url: http://localhost:3333
|
||||
subdomain: mongs
|
||||
image: images/mongo.png
|
||||
|
||||
-
|
||||
name: ipython
|
||||
url: http://localhost:8888
|
||||
subdomain: ipython
|
||||
image: images/ipython.png
|
||||
`)
|
||||
if was, err := loadWebAppServersFromYamlFile(yml); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(was) != 2 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
---
|
||||
|
||||
# This yaml file where your need to define your services. Look at the *http bin*
|
||||
# example for fields one needs to give to define a service
|
||||
#
|
||||
# Before wishing to use *bab*, one needs to setup correctly its DNS server to points
|
||||
# the subdomains to the correct ips. For localhost case, we recommend changing the
|
||||
# /etc/hosts file to have a file similar to the one bellow
|
||||
# 127.0.0.1 localhost httpbin.localhost
|
||||
|
||||
-
|
||||
name: http bin # service's name as it will be shown to the user
|
||||
url: http://httpbin.org # service's url
|
||||
subdomain: httpbin # subdomain where the service will be reachable
|
||||
image: images/bin.png # service's imagae path relative to the client directory
|
||||
@@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
type WebAppServer struct {
|
||||
Subdomain string `yaml:"subdomain"`
|
||||
WebAppURL string `yaml:"url"`
|
||||
ImageUrl string `yaml:"image"`
|
||||
Name string `yaml:"name"`
|
||||
Domain string `yaml:"default_domain,omitempty"`
|
||||
}
|
||||
|
||||
func (this *WebAppServer) Localize(domain string) *WebAppServer {
|
||||
return &WebAppServer{this.Subdomain, this.WebAppURL, this.ImageUrl, this.Name, domain}
|
||||
}
|
||||
|
||||
type jsonWebAppServer struct {
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
func (this *WebAppServer) copyHeader(from, to http.Header) {
|
||||
for key, val := range from {
|
||||
to.Set(key, strings.Join(val, ";"))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *WebAppServer) GetWebAppBaseUrl() string {
|
||||
return fmt.Sprintf("http://%s.%s", this.Subdomain, this.Domain)
|
||||
}
|
||||
|
||||
func (this *WebAppServer) MarshalJSON() ([]byte, error) {
|
||||
ret := &jsonWebAppServer{this.Name,
|
||||
this.GetWebAppBaseUrl(),
|
||||
this.ImageUrl}
|
||||
return json.Marshal(ret)
|
||||
}
|
||||
|
||||
func (this *WebAppServer) HandleGet(w http.ResponseWriter, request *http.Request) {
|
||||
URL := this.WebAppURL + request.URL.Path
|
||||
if request.URL.RawQuery != "" {
|
||||
URL += "?" + request.URL.RawQuery
|
||||
}
|
||||
req, err := http.NewRequest(request.Method, URL, request.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, "bab error :(", err.Error())
|
||||
return
|
||||
}
|
||||
this.copyHeader(request.Header, req.Header)
|
||||
resp, err := new(http.Client).Do(req)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, "bab error :(\n", err.Error())
|
||||
return
|
||||
}
|
||||
this.copyHeader(resp.Header, w.Header())
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
defer resp.Body.Close()
|
||||
io.Copy(w, resp.Body)
|
||||
}
|
||||
|
||||
func (this *WebAppServer) Handle(w http.ResponseWriter, request *http.Request) {
|
||||
switch request.Method {
|
||||
case "GET":
|
||||
this.HandleGet(w, request)
|
||||
case "POST":
|
||||
this.HandleGet(w, request)
|
||||
case "PUT":
|
||||
this.HandleGet(w, request)
|
||||
default:
|
||||
w.WriteHeader(500)
|
||||
fmt.Fprint(w, "method "+request.Method+" is not yet available")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (this *WebAppServer) CanHandle(host string) bool {
|
||||
r := regexp.MustCompile(`[:\.]`)
|
||||
domain := r.Split(host, -1)
|
||||
if len(domain) < 3 {
|
||||
return false
|
||||
}
|
||||
return domain[len(domain)-3] == this.Subdomain
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type TestServer struct {
|
||||
Server *httptest.Server
|
||||
Get func(w http.ResponseWriter, request *http.Request)
|
||||
Post func(w http.ResponseWriter, request *http.Request)
|
||||
T *testing.T
|
||||
}
|
||||
|
||||
func (this *TestServer) ServeHTTP(w http.ResponseWriter, request *http.Request) {
|
||||
if request.Method == "GET" {
|
||||
this.Get(w, request)
|
||||
} else if request.Method == "POST" {
|
||||
this.Post(w, request)
|
||||
} else {
|
||||
this.T.Error("cannot execute ", request.Method, " method")
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TestServer) HelloWorld(w http.ResponseWriter, request *http.Request) {
|
||||
fmt.Fprint(w, "hello world\n")
|
||||
}
|
||||
|
||||
func (this *TestServer) ReturnPath(w http.ResponseWriter, request *http.Request) {
|
||||
fmt.Fprint(w, request.URL.Path)
|
||||
}
|
||||
|
||||
func (this *TestServer) ReturnBogusHeader(w http.ResponseWriter, request *http.Request) {
|
||||
fmt.Fprint(w, request.Header.Get("Bogus"))
|
||||
}
|
||||
|
||||
func (this *TestServer) ReturnBody(w http.ResponseWriter, request *http.Request) {
|
||||
io.Copy(w, request.Body)
|
||||
|
||||
}
|
||||
|
||||
func (this *TestServer) SetBogusHeader(w http.ResponseWriter, request *http.Request) {
|
||||
w.Header().Set("Bogus", "has been set on the server")
|
||||
fmt.Fprint(w, request.Header.Get("Bogus"))
|
||||
}
|
||||
|
||||
func (this *TestServer) ReturnParamValue(w http.ResponseWriter, request *http.Request) {
|
||||
if err := request.ParseForm(); err != nil {
|
||||
this.T.Error(err)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, request.Form.Get("search"))
|
||||
}
|
||||
|
||||
func GetTestServer(t *testing.T) *TestServer {
|
||||
ret := new(TestServer)
|
||||
ret.T = t
|
||||
server := httptest.NewServer(ret)
|
||||
ret.Server = server
|
||||
return ret
|
||||
}
|
||||
|
||||
func TestMarshalJSON(t *testing.T) {
|
||||
wapp := &WebAppServer{"big", "http://bigtits.com", "img.png", "some name", "tits.com:8000"}
|
||||
b, err := json.Marshal(wapp)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if s := string(b); !strings.Contains(s, `"http://big.tits.com:8000"`) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanHandle(t *testing.T) {
|
||||
was := &WebAppServer{Subdomain: "foo"}
|
||||
exps := map[string]bool{"foo.bigtits.com": true,
|
||||
"foo.localhost:8000": true,
|
||||
"bar.bigtits.com": false,
|
||||
"bigtits.com": false}
|
||||
for host, exp := range exps {
|
||||
if was.CanHandle(host) != exp {
|
||||
t.Error("error handeling ", host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetHelloWorld(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
ts.Get = ts.HelloWorld
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://foo.bigtits.com/natasha", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
was.HandleGet(w, req)
|
||||
if body := string(w.Body.Bytes()); body != "hello world\n" {
|
||||
t.Error(body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetPath(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
ts.Get = ts.ReturnPath
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://foo.bigtits.com/natasha", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
was.HandleGet(w, req)
|
||||
if body := string(w.Body.Bytes()); body != "/natasha" {
|
||||
t.Error(body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetBogusHeader(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
ts.Get = ts.ReturnBogusHeader
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://foo.bigtits.com/natasha", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
req.Header.Set("Bogus", "something in the header!")
|
||||
was.HandleGet(w, req)
|
||||
if body := string(w.Body.Bytes()); body != "something in the header!" {
|
||||
t.Error(body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetSetBogusHeader(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
ts.Get = ts.SetBogusHeader
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://foo.bigtits.com/natasha", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
was.HandleGet(w, req)
|
||||
if header := w.Header().Get("Bogus"); header != "has been set on the server" {
|
||||
t.Error(header)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetReturnParamValue(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
ts.Get = ts.ReturnParamValue
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://foo.bigtits.com/natasha?search=bigtits", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
was.HandleGet(w, req)
|
||||
if body := string(w.Body.Bytes()); body != "bigtits" {
|
||||
t.Error(body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePostWithBody(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
ts.Post = ts.ReturnBody
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("POST", "http://foo.bigtits.com/natasha", bytes.NewBuffer([]byte(`hello world in the body`)))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
was.Handle(w, req)
|
||||
if body := string(w.Body.Bytes()); body != "hello world in the body" {
|
||||
t.Error("wrong body :", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePostBogusHeader(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
ts.Post = ts.SetBogusHeader
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("POST", "http://foo.bigtits.com/natasha", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
was.HandleGet(w, req)
|
||||
if header := w.Header().Get("Bogus"); header != "has been set on the server" {
|
||||
t.Error(header)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanHandleNoService(t *testing.T) {
|
||||
ts := GetTestServer(t)
|
||||
|
||||
was := &WebAppServer{Subdomain: "foo", WebAppURL: ts.Server.URL}
|
||||
ts.Server.Close()
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "http://foo.bigtits.com/natasha?search=bigtits", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
was.HandleGet(w, req)
|
||||
if w.Code != 500 {
|
||||
t.Error("status should be 500, service not found")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user