web application base layout

This commit is contained in:
2018-12-06 22:12:32 +01:00
parent e3bfd27b0a
commit 0f0213c326
17 changed files with 240 additions and 32 deletions

View File

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Jeremy Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,10 @@ import (
)
type config struct {
HTTPHost string `env:"ORION_HTTP_HOST"`
HTTPPort string `env:"ORION_HTTP_PORT"`
HTTPHost string `env:"ORION_HTTP_HOST"`
HTTPPort string `env:"ORION_HTTP_PORT"`
TemplateDir string `env:"ORION_TEMPLATE_DIR"`
AssetDir string `env:"ORION_ASSET_DIR"`
}
func overwriteFromEnv(conf *config) error {
@ -18,7 +20,9 @@ func overwriteFromEnv(conf *config) error {
func newDefaultConfig() *config {
return &config{
HTTPHost: "0.0.0.0",
HTTPPort: "8888",
HTTPHost: "0.0.0.0",
HTTPPort: "8888",
TemplateDir: "./templates",
AssetDir: "./assets",
}
}

View File

@ -1,4 +1,4 @@
package rpc
package jsonrpc
import (
"context"
@ -7,10 +7,10 @@ import (
"net/http"
"time"
"forge.cadoles.com/Cadoles/owrt"
"forge.cadoles.com/Pyxis/orion/emlid"
"forge.cadoles.com/Pyxis/orion/emlid/reachview"
"forge.cadoles.com/Pyxis/orion/emlid/updater"
"forge.cadoles.com/Pyxis/orion/openwrt"
"github.com/gorilla/rpc"
"github.com/gorilla/rpc/json"
"github.com/pkg/errors"
@ -18,12 +18,12 @@ import (
// OrionService is the JSON-RPC API
type OrionService struct {
UCI *openwrt.UCI
UCI *owrt.UCI
}
// NewOrionService create a new OrionService !
func NewOrionService() *OrionService {
uci := openwrt.NewUCI()
uci := owrt.NewUCI()
return &OrionService{
UCI: uci,
}
@ -50,7 +50,7 @@ type ListIfaceArgs struct{}
// ListIfaceResponse is the response structure for exposed method OwrtListWifiDevices
type ListIfaceResponse struct {
Interfaces []*openwrt.UCIWirelessInterface
Interfaces map[int]*owrt.UCIWirelessInterface
}
// OwrtListWifiInterfaces offers an RPC Method to list wifi interfaces in a OpenWRT device.
@ -63,13 +63,13 @@ func (o *OrionService) OwrtListWifiInterfaces(r *http.Request, args *ListIfaceAr
// CreateIfaceArgs argument structure for exported method OwrtCreateWifiInterface
type CreateIfaceArgs struct {
Cleanup bool
Iface *openwrt.UCIWirelessInterface
Iface *owrt.UCIWirelessInterface
}
// CreateIfaceResponse argument structure for exported method OwrtCreateWifiInterface
type CreateIfaceResponse struct {
Errors []*openwrt.Action
Iface *openwrt.UCIWirelessInterface
Errors []*owrt.Action
Iface *owrt.UCIWirelessInterface
}
// OwrtCreateWifiInterface Create a WifiInterface in openwrt
@ -124,7 +124,7 @@ func (o *OrionService) OwrtCreateWifiInterface(r *http.Request,
// ConnectIfaceArgs argument structure for exported method OwrtCreateWifiInterface
type ConnectIfaceArgs struct {
Iface *openwrt.UCIWirelessInterface
Iface *owrt.UCIWirelessInterface
SSID string
Key string
}
@ -175,7 +175,7 @@ type OrionServer struct {
SSID string
Security string
WifiKey string
ClientIface *openwrt.UCIWirelessInterface
ClientIface *owrt.UCIWirelessInterface
}
// UpdateOrionBoxArgs argument structure for exported method OwrtCreateWifiInterface
@ -212,7 +212,7 @@ func (o *OrionService) connectBox(box *OrionBox, server *OrionServer) error {
if cn.ReturnCode != 0 {
return fmt.Errorf("%s\n%s", cn.Stdout, cn.Stderr)
}
dhcli := openwrt.NewDhcpClient(iface.SysDevName)
dhcli := owrt.NewDhcpClient(iface.SysDevName)
dhres := dhcli.AskForIP()
if dhres.CmdRes.ReturnCode != 0 {
return fmt.Errorf("%s\n%s", cn.Stdout, cn.Stderr)

View File

@ -0,0 +1,34 @@
package jsonrpc
import (
"forge.cadoles.com/wpetit/goweb/service"
"github.com/gorilla/rpc"
"github.com/pkg/errors"
)
const ServiceName service.Name = "jsonrpc"
// From retrieves the JSONRPC service in the given container or panic
func From(container *service.Container) (*rpc.Server, error) {
service, err := container.Service(ServiceName)
if err != nil {
return nil, errors.Wrapf(err, "error while retrieving '%s' service", ServiceName)
}
jsonRPCService, ok := service.(*rpc.Server)
if !ok {
return nil, errors.Errorf("retrieved service is not a valid '%s' service", ServiceName)
}
return jsonRPCService, nil
}
// Must retrieves the json-rpc service in the given container or panic otherwise
func Must(container *service.Container) *rpc.Server {
service, err := From(container)
if err != nil {
panic(err)
}
return service
}

View File

@ -4,15 +4,28 @@ import (
"fmt"
"log"
"net/http"
"net/rpc"
"github.com/gorilla/sessions"
"forge.cadoles.com/wpetit/goweb/static"
"forge.cadoles.com/wpetit/goweb/session/gorilla"
"forge.cadoles.com/wpetit/goweb/template/html"
"forge.cadoles.com/Pyxis/orion/cmd/server/rpc"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
goweb "forge.cadoles.com/wpetit/goweb/middleware"
"forge.cadoles.com/wpetit/goweb/service/session"
"forge.cadoles.com/wpetit/goweb/service/template"
"forge.cadoles.com/Pyxis/orion/cmd/server/jsonrpc"
"forge.cadoles.com/wpetit/goweb/service"
"github.com/go-chi/chi"
)
var (
conf = newDefaultConfig()
jsonrpc = rpc.NewServer()
conf = newDefaultConfig()
)
func main() {
@ -21,24 +34,80 @@ func main() {
log.Fatal(err)
}
r := chi.NewRouter()
// Create service container
container := service.NewContainer()
container.Provide(jsonrpc.ServiceName, getJSONRPCServiceProvider())
container.Provide(template.ServiceName, getTemplateServiceProvider(conf.TemplateDir))
container.Provide(session.ServiceName, getSessionServiceProvider())
r.Use(middleware.Recoverer)
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
router := chi.NewRouter()
r.Post("/rpc", handleRPC)
router.Use(middleware.Recoverer)
router.Use(middleware.RequestID)
router.Use(middleware.Logger)
router.Use(goweb.ServiceContainer(container))
// Define routes
router.Get("/", serveHomepage)
router.Post("/rpc", handleJSONRPC)
router.Get("/**", static.Dir(conf.AssetDir, "", router.NotFoundHandler()))
hostStr := fmt.Sprintf("%s:%s", conf.HTTPHost, conf.HTTPPort)
log.Printf("listening on http://%s", hostStr)
if err := http.ListenAndServe(hostStr, r); err != nil {
log.Printf("listening on http://%s", hostStr)
if err := http.ListenAndServe(hostStr, router); err != nil {
log.Fatal(err)
}
}
func handleRPC(w http.ResponseWriter, r *http.Request) {
jsonrpc.ServeHTTP(w, r)
func getJSONRPCServiceProvider() service.Provider {
jsonrpc := rpc.NewServer()
return func(c *service.Container) (interface{}, error) {
return jsonrpc, nil
}
}
func getTemplateServiceProvider(templateDir string) service.Provider {
// Create templateService at application startup
templateService := html.NewTemplateService()
// Load templates and keep error in cache
err := templateService.LoadTemplates(templateDir)
return func(c *service.Container) (interface{}, error) {
// If an error occured during the templates loading
// return the error
if err != nil {
return nil, err
}
return templateService, nil
}
}
func getSessionServiceProvider() service.Provider {
// Create a new encrypted/authenticated cookie store for the session
cookieStore, err := gorilla.CreateCookieSessionStore(32, 64)
var sessionService session.Service
if err == nil {
sessionService = gorilla.NewSessionService(
"orion",
cookieStore,
&sessions.Options{
HttpOnly: true,
},
)
}
return func(c *service.Container) (interface{}, error) {
if err != nil {
return nil, err
}
return sessionService, nil
}
}

33
cmd/server/route.go Normal file
View File

@ -0,0 +1,33 @@
package main
import (
"net/http"
"forge.cadoles.com/wpetit/goweb/middleware"
"forge.cadoles.com/wpetit/goweb/service"
"forge.cadoles.com/wpetit/goweb/service/template"
"forge.cadoles.com/Pyxis/orion/cmd/server/jsonrpc"
)
func serveHomepage(w http.ResponseWriter, r *http.Request) {
container := getServiceContainer(r)
templateService := template.Must(container)
if err := templateService.RenderPage(w, "home.html.tmpl", nil); err != nil {
panic(err)
}
}
func handleJSONRPC(w http.ResponseWriter, r *http.Request) {
container := getServiceContainer(r)
rpcServer := jsonrpc.Must(container)
rpcServer.ServeHTTP(w, r)
}
func getServiceContainer(r *http.Request) *service.Container {
container, err := middleware.GetServiceContainer(r.Context())
if err != nil {
panic(err)
}
return container
}

View File

@ -0,0 +1,23 @@
{{define "base"}}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orion</title>
<link rel="stylesheet" href="/vendor/bulma-0.7.2/css/bulma.min.css">
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">
Hello World
</h1>
<p class="subtitle">
My first website with <strong>Bulma</strong>!
</p>
</div>
</section>
</body>
</html>
{{end}}

View File

@ -0,0 +1 @@
{{template "base" .}}