convert to echo
This commit is contained in:
parent
f5255969a7
commit
6ce4c20736
43 changed files with 2615 additions and 544 deletions
11
.drone.yml
11
.drone.yml
|
@ -7,9 +7,14 @@ pipeline:
|
|||
pull: true
|
||||
commands:
|
||||
- mkdir -p /go/bin /go/src
|
||||
- go get github.com/revel/cmd/revel
|
||||
- revel build github.com/CyCoreSystems/cycore-web ../tmp prod
|
||||
- mv ../tmp .
|
||||
- go get -u github.com/golang/dep/cmd/dep/...
|
||||
- go get -u github.com/alecthomas/gometalinter/...
|
||||
- go get -u github.com/mjibson/esc
|
||||
- dep ensure
|
||||
- go generate
|
||||
- gometalinter --install
|
||||
- gometalinter --vendor ./...
|
||||
- go build
|
||||
publish:
|
||||
image: plugins/docker:17.05
|
||||
repo: quay.io/cycore/web
|
||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,4 +1,2 @@
|
|||
test-results/
|
||||
tmp/
|
||||
routes/
|
||||
/vendor/
|
||||
/cycore-web
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
FROM ulexus/go-minimal
|
||||
COPY tmp/cycore-web /app
|
||||
COPY tmp/src /src
|
||||
COPY cycore-web /app
|
||||
COPY assets /assets
|
||||
|
|
99
Gopkg.lock
generated
Normal file
99
Gopkg.lock
generated
Normal file
|
@ -0,0 +1,99 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/CyCoreSystems/sendinblue"
|
||||
packages = ["."]
|
||||
revision = "9b851c8f8e26b6bfc746aaa0dbaf8f7a78286ee0"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/jmoiron/sqlx"
|
||||
packages = [".","reflectx"]
|
||||
revision = "2aeb6a910c2b94f2d5eb53d9895d80e27264ec41"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/labstack/echo"
|
||||
packages = [".","middleware"]
|
||||
revision = "6d227dfea4d2e52cb76856120b3c17f758139b4e"
|
||||
version = "3.3.5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/labstack/gommon"
|
||||
packages = ["bytes","color","log","random"]
|
||||
revision = "588f4e8bddc6cb45c27b448e925c7fd6a5545434"
|
||||
version = "0.2.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
packages = [".","oid"]
|
||||
revision = "d34b9ff171c21ad295489235aec8b6626023cd04"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/valyala/bytebufferpool"
|
||||
packages = ["."]
|
||||
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/valyala/fasttemplate"
|
||||
packages = ["."]
|
||||
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/atomic"
|
||||
packages = ["."]
|
||||
revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289"
|
||||
version = "v1.3.2"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/multierr"
|
||||
packages = ["."]
|
||||
revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "go.uber.org/zap"
|
||||
packages = [".","buffer","internal/bufferpool","internal/color","internal/exit","zapcore"]
|
||||
revision = "eeedf312bc6c57391d84767a4cd413f02a917974"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["acme","acme/autocert"]
|
||||
revision = "1a580b3eff7814fc9b40602fd35256c63b50f491"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "7c87d13f8e835d2fb3a70a2912c811ed0c1d241b"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "663f98ffe10e2e1074511774007b7ec79196f112e44ab874c8e209c2ee0d9453"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
42
Gopkg.toml
Normal file
42
Gopkg.toml
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/CyCoreSystems/sendinblue"
|
||||
version = "0.1.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/jmoiron/sqlx"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/labstack/echo"
|
||||
version = "3.3.5"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
|
||||
[[constraint]]
|
||||
name = "go.uber.org/zap"
|
||||
version = "1.8.0"
|
|
@ -1,11 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import "github.com/revel/revel"
|
||||
|
||||
type App struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
func (c App) Index() revel.Result {
|
||||
return c.Render()
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/CyCoreSystems/cycore-web/app/routes"
|
||||
"github.com/CyCoreSystems/sendinblue"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
var contactEmailT *template.Template
|
||||
|
||||
func init() {
|
||||
contactEmailT = template.Must(template.New("contactEmail").Parse(contactEmailTemplate))
|
||||
}
|
||||
|
||||
// Contact controller handles customer contact operations
|
||||
type Contact struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// ContactRequest handles a customer contact request
|
||||
func (c Contact) ContactRequest(name, email string) revel.Result {
|
||||
|
||||
c.Log.Info("received contact request", "name", name, "email", email, "source", c.ClientIP)
|
||||
|
||||
if name == "" {
|
||||
c.Flash.Error("Please supply a name")
|
||||
return c.Redirect(routes.App.Index())
|
||||
}
|
||||
if email == "" {
|
||||
c.Flash.Error("Please supply an email address")
|
||||
return c.Redirect(routes.App.Index())
|
||||
}
|
||||
|
||||
emailBody, err := c.renderContactEmail(name, email)
|
||||
if err != nil {
|
||||
c.Log.Error("failed to render contact email", "error", err)
|
||||
c.Flash.Error("Internal error encountered; please try again")
|
||||
return c.Redirect(routes.App.Index())
|
||||
}
|
||||
|
||||
msg := &sendinblue.Message{
|
||||
Sender: &sendinblue.Address{
|
||||
Name: "CyCore Systems, Inc",
|
||||
Email: "sys@cycoresys.com",
|
||||
},
|
||||
To: c.getEmailContacts(),
|
||||
Subject: "Contact Request",
|
||||
HTMLContent: emailBody,
|
||||
Tags: []string{"contact-request"},
|
||||
}
|
||||
if err = msg.Send(os.Getenv("SENDINBLUE_APIKEY")); err != nil {
|
||||
c.Log.Error("failed to send contact email", "error", err)
|
||||
c.Flash.Error("Request failed")
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(msg)
|
||||
|
||||
return c.Redirect(routes.App.Index())
|
||||
}
|
||||
|
||||
c.Flash.Success("Request sent")
|
||||
return c.Redirect(routes.App.Index())
|
||||
}
|
||||
|
||||
func (c Contact) renderContactEmail(name, email string) (string, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
err := contactEmailT.Execute(buf, struct {
|
||||
Name string
|
||||
Email string
|
||||
Timestamp string
|
||||
}{
|
||||
Name: name,
|
||||
Email: email,
|
||||
Timestamp: time.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (c Contact) getEmailContacts() []*sendinblue.Address {
|
||||
|
||||
var ret []*sendinblue.Address
|
||||
if err := json.Unmarshal([]byte(os.Getenv("CONTACT_RECIPIENTS")), &ret); err != nil {
|
||||
|
||||
// Fall back to default if we fail to load from environment
|
||||
c.Log.Warn("failed to load recipients from environment", "error", err)
|
||||
ret = append(ret, &sendinblue.Address{
|
||||
Name: "System Receiver",
|
||||
Email: "sys@cycoresys.com",
|
||||
})
|
||||
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var contactEmailTemplate = `
|
||||
<html>
|
||||
<body>
|
||||
<h3>Web Contact Form</h3>
|
||||
<ul>
|
||||
<li><b>Name:</b> {{.Name}}</li>
|
||||
<li><b>Email:</b> {{.Email}}</li>
|
||||
<li><b>Timestamp:</b> {{.Timestamp}}</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
`
|
59
app/init.go
59
app/init.go
|
@ -1,59 +0,0 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/revel/log15"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Filters is the default set of global filters.
|
||||
revel.Filters = []revel.Filter{
|
||||
revel.PanicFilter, // Recover from panics and display an error page instead.
|
||||
revel.RouterFilter, // Use the routing table to select the right Action
|
||||
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
|
||||
revel.ParamsFilter, // Parse parameters into Controller.Params.
|
||||
revel.SessionFilter, // Restore and write the session cookie.
|
||||
revel.FlashFilter, // Restore and write the flash cookie.
|
||||
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
|
||||
//revel.I18nFilter, // Resolve the requested language
|
||||
HeaderFilter, // Add some security based headers
|
||||
revel.InterceptorFilter, // Run interceptors around the action.
|
||||
revel.CompressFilter, // Compress the result.
|
||||
revel.ActionInvoker, // Invoke the action.
|
||||
}
|
||||
|
||||
// register startup functions with OnAppStart
|
||||
// ( order dependent )
|
||||
// revel.OnAppStart(InitDB)
|
||||
// revel.OnAppStart(FillCache)
|
||||
revel.OnAppStart(InitLogger)
|
||||
}
|
||||
|
||||
// HeaderFilter is a default set of headers to be added
|
||||
// TODO turn this into revel.HeaderFilter
|
||||
// should probably also have a filter for CSRF
|
||||
// not sure if it can go in the same filter or not
|
||||
var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
|
||||
// Add some common security headers
|
||||
c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN")
|
||||
c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block")
|
||||
c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff")
|
||||
|
||||
fc[0](c, fc[1:]) // Execute the next filter stage.
|
||||
}
|
||||
|
||||
// InitLogger initializes the logging handler
|
||||
func InitLogger() {
|
||||
|
||||
// If we are running inside kubernetes, use the oklog logger
|
||||
if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
|
||||
h, err := log15.NetHandler("tcp", "oklog.log:7651", log15.JsonFormat())
|
||||
if err != nil {
|
||||
revel.AppLog.Error("failed to construct network log handler", "error", err)
|
||||
return
|
||||
}
|
||||
revel.RevelLog.SetHandler(h)
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
<style type="text/css">
|
||||
#sidebar {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top:69px;
|
||||
max-width: 75%;
|
||||
z-index: 1000;
|
||||
background-color: #fee;
|
||||
border: thin solid grey;
|
||||
padding: 10px;
|
||||
}
|
||||
#toggleSidebar {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 50px;
|
||||
background-color: #fee;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="sidebar" style="display:none;">
|
||||
<h4>Available pipelines</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
<h4>Flash</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .flash}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
|
||||
<h4>Errors</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .errors}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
</div>
|
||||
<a id="toggleSidebar" href="#" class="toggles"><i class="glyphicon glyphicon-chevron-left"></i></a>
|
||||
|
||||
<script>
|
||||
$sidebar = 0;
|
||||
$('#toggleSidebar').click(function() {
|
||||
if ($sidebar === 1) {
|
||||
$('#sidebar').hide();
|
||||
$('#toggleSidebar i').addClass('glyphicon-chevron-left');
|
||||
$('#toggleSidebar i').removeClass('glyphicon-chevron-right');
|
||||
$sidebar = 0;
|
||||
}
|
||||
else {
|
||||
$('#sidebar').show();
|
||||
$('#toggleSidebar i').addClass('glyphicon-chevron-right');
|
||||
$('#toggleSidebar i').removeClass('glyphicon-chevron-left');
|
||||
$sidebar = 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
</script>
|
|
@ -1,25 +0,0 @@
|
|||
|
||||
<div class="footer">
|
||||
<div class="credit">
|
||||
<a href="http://www.cycoresys.com">
|
||||
Site by CyCore Systems
|
||||
</a>
|
||||
</div>
|
||||
<div class="copy">
|
||||
© 2016 CyCore Systems
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="social">
|
||||
<a href="">
|
||||
<i class="fa fa-twitter"></i>
|
||||
</a>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
{{if eq .RunMode "dev"}}
|
||||
{{template "debug.html" .}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
12
assets/css/alert.import.less
vendored
Normal file
12
assets/css/alert.import.less
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
.alert {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
color: @red;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
color: @green;
|
||||
}
|
1
assets/css/anim.import.less
vendored
Normal file
1
assets/css/anim.import.less
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
//ANIMATIONS
|
164
assets/css/base.import.less
vendored
Normal file
164
assets/css/base.import.less
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
|
||||
//PRESETS
|
||||
|
||||
body, div, nav, ul, li, a, label, span, table {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
float: left;
|
||||
box-sizing: border-box;
|
||||
background-clip: border-box;
|
||||
}
|
||||
body {
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
background: @background;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
div {
|
||||
float: left;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
padding: 5px 10px;
|
||||
.border-radius(7px);
|
||||
.fasttrans;
|
||||
color: @key;
|
||||
}
|
||||
a:hover { .fasttrans; color: @keylight;}
|
||||
|
||||
.hide { display: none; }
|
||||
|
||||
//GRID COLUMNS
|
||||
|
||||
.twelve { .column(12); }
|
||||
.ten { .column(10); }
|
||||
.nine { .column(9); }
|
||||
.eight { .column(8); }
|
||||
.six { .column(6); }
|
||||
.five { .column(5); }
|
||||
.four { .column(4); }
|
||||
.three { .column(3); }
|
||||
.two { .column(2); }
|
||||
|
||||
//STRUCTURES
|
||||
|
||||
#page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 500px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.unit { //used for tables
|
||||
overflow: hidden;
|
||||
.background-linear-gradient(#fff 25%, @lightgrey);
|
||||
border: 1px solid @lightgrey;
|
||||
.border-radius(5px);
|
||||
.box-shadow(1px 1px 1px 0 rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
.contentunit {
|
||||
.background-linear-gradient(white, fadeout(@keylight, 98%));
|
||||
.border-radius(10px);
|
||||
border: 2px solid fadeout(@keylight, 60%);
|
||||
}
|
||||
.transcontentunit {
|
||||
.contentunit;
|
||||
background: transparent none;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
.colorcontentunit {
|
||||
.contentunit;
|
||||
background: @key;
|
||||
border: 2px solid @key;
|
||||
font-size: 1.3em;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 40%;
|
||||
text-align: right;
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
|
||||
i {
|
||||
color: white;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
background-color: @key;
|
||||
.border-radius(150px);
|
||||
.box-shadow(0px 1px 2px 0 @darkestgrey);
|
||||
.fasttrans;
|
||||
}
|
||||
i:hover { background-color: @keylight; .fasttrans; }
|
||||
|
||||
.social i {
|
||||
font-size: 26px;
|
||||
height: 32px;
|
||||
width: 42px;
|
||||
padding-top: 10px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hide { display: none; }
|
||||
|
||||
.tag {
|
||||
display: block;
|
||||
background: fadeout(@keylight, 80%);
|
||||
.border-radius(2px);
|
||||
padding: 2px 5px;
|
||||
margin: 2px 10px 2px 0;
|
||||
border: 1px solid @lightestgrey;
|
||||
color: @darkgrey;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: relative;
|
||||
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 50px 30px 0 30px;
|
||||
|
||||
.copy, .credit {
|
||||
.sans;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 30px;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.credit a {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 30px;
|
||||
color: @key;
|
||||
}
|
||||
.credit a:hover {
|
||||
color: @keydark;
|
||||
}
|
||||
.copy {
|
||||
color: @darkgrey;
|
||||
float: right;
|
||||
}
|
||||
.login {
|
||||
float: right;
|
||||
|
||||
a { .sans; }
|
||||
}
|
||||
|
||||
.social {
|
||||
float: right;
|
||||
height: 100%;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
}
|
||||
|
82
assets/css/button.import.less
vendored
Normal file
82
assets/css/button.import.less
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
|
||||
//BUTTONS
|
||||
|
||||
button { border: 0 none; }
|
||||
|
||||
button.demo {
|
||||
background-color: @key;
|
||||
border-top: 1px solid @keylight;
|
||||
border-bottom: 1px solid @transblack;
|
||||
.box-shadow(
|
||||
inset 0 0 20px 5px @transblack,
|
||||
inset 0 0 0 0 @keylight,
|
||||
0 2px 4px 0 @lightblack);
|
||||
text-shadow: -1px -1px 0 black, 1px 1px 0 @keylight;
|
||||
|
||||
color: white;
|
||||
|
||||
.slowtrans;
|
||||
|
||||
i {
|
||||
color: @key;
|
||||
font-size: 85px;
|
||||
text-shadow: 0 0 0 transparent;
|
||||
vertical-align: bottom;
|
||||
position: relative;
|
||||
top: -11px;
|
||||
}
|
||||
|
||||
}
|
||||
button.demo:hover {
|
||||
// background-color: @keylight;
|
||||
.box-shadow(
|
||||
inset 0 0 20px 5px @transblack,
|
||||
inset 0px 10px 50px 0 fadeout(@keylight, 70%),
|
||||
0 2px 4px 0 @lightblack);
|
||||
text-shadow: -1px -1px 0 black, 1px 1px 0 @key;
|
||||
|
||||
.slowtrans;
|
||||
|
||||
i {
|
||||
color: white;
|
||||
text-shadow: 0 0 4px @key;
|
||||
.fasttrans;
|
||||
}
|
||||
}
|
||||
button.large {
|
||||
width: 800px;
|
||||
margin: 0 auto;
|
||||
height: 100px;
|
||||
.border-radius(8px);
|
||||
padding-top: 7px;
|
||||
|
||||
font-size: 80px;
|
||||
line-height: 100px;
|
||||
|
||||
}
|
||||
button.medium {
|
||||
.column(5, 8);
|
||||
.push(2);
|
||||
height: 60px;
|
||||
.border-radius(6px);
|
||||
margin-top: 20px;
|
||||
|
||||
font-size: 40px;
|
||||
line-height: 60px;
|
||||
|
||||
i { font-size: 50px; top: -6px; }
|
||||
}
|
||||
button.green:hover { background-color: @green; .fasttrans; }
|
||||
button.small {
|
||||
.column(2);
|
||||
.push(2);
|
||||
margin-top: 100px;
|
||||
height: 50px;
|
||||
.border-radius(6px);
|
||||
font-size: 40px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
button:focus { outline: none; }
|
||||
|
||||
|
76
assets/css/fonts.import.less
vendored
Normal file
76
assets/css/fonts.import.less
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
//CHANGE IMPORT AND open/sans/bold
|
||||
|
||||
@import url(https://fonts.googleapis.com/css?family=Montserrat|Rokkitt:700|Rubik+Mono+One);
|
||||
|
||||
.open { font-family: Verdana, sans-serif; }
|
||||
.sans { font-family: Monserrat, sans-serif; }
|
||||
.bold { font-family: Rokkitt, sans-serif; }
|
||||
|
||||
* {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
.nav a { .open; }
|
||||
|
||||
.brand {
|
||||
.open;
|
||||
display: block;
|
||||
color: @grey;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
line-height: 48px;
|
||||
}
|
||||
.light {
|
||||
color: white;
|
||||
text-shadow: 1px 1px 2px @darkgrey, -1px -1px 1px @darkestgrey;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 { .open; }
|
||||
|
||||
h1 {
|
||||
font-size: 5em;
|
||||
position: relative;
|
||||
color: @red;
|
||||
word-wrap: break-word;
|
||||
font-weight: normal;
|
||||
line-height: 2em;
|
||||
text-shadow: -1px -1px 0 black;
|
||||
}
|
||||
h2 {
|
||||
font-size: 3em;
|
||||
line-height: 1.2em;
|
||||
color: @key;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.2em;
|
||||
padding-left: 10px; padding-right: 10px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 2.2em;
|
||||
line-height: 1em;
|
||||
color: @keylight;
|
||||
text-align: center;
|
||||
}
|
||||
h5, h6 { color: @darkergrey; }
|
||||
h5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
h6 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
em {
|
||||
color: @keydark;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
padding: 2px;
|
||||
.border-radius(2px);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.fix { color: @red; }
|
||||
|
34
assets/css/forms.import.less
vendored
Normal file
34
assets/css/forms.import.less
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
label, .label, input, select, textarea {
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
display: inline-block;
|
||||
.border-radius(3px);
|
||||
vertical-align: top;
|
||||
padding: 5px 10px;
|
||||
border: 0 none;
|
||||
float: left;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
|
||||
}
|
||||
|
||||
input {
|
||||
border: 1px solid @lightgrey;
|
||||
}
|
||||
input[type=submit] {
|
||||
.bold;
|
||||
padding: 7px 20px;
|
||||
margin-left: 10px;
|
||||
background: @keylight;
|
||||
color: white;
|
||||
.fasttrans;
|
||||
}
|
||||
input[type=submit]:hover, input[type=submit]:active {
|
||||
background: saturate(@contrast, 20%);
|
||||
.fasttrans;
|
||||
}
|
||||
|
||||
|
||||
|
65
assets/css/grid.import.less
vendored
Normal file
65
assets/css/grid.import.less
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
/////////////////
|
||||
// Semantic.gs // for LESS: http://lesscss.org/
|
||||
/////////////////
|
||||
|
||||
// Defaults which you can freely override
|
||||
@column-width: 60;
|
||||
@gutter-width: 0;
|
||||
@columns: 12;
|
||||
|
||||
// Utility variable — you should never need to modify this
|
||||
@gridsystem-width: (@column-width*@columns) + (@gutter-width*@columns) * 1px;
|
||||
|
||||
// Set @total-width to 100% for a fluid layout
|
||||
@total-width: 100%;
|
||||
|
||||
// Uncomment these two lines and the star-hack width/margin lines below to enable sub-pixel fix for IE6 & 7. See http://tylertate.com/blog/2012/01/05/subpixel-rounding.html
|
||||
// @min-width: 960;
|
||||
// @correction: 0.5 / @min-width * 100 * 1%;
|
||||
|
||||
// The micro clearfix http://nicolasgallagher.com/micro-clearfix-hack/
|
||||
.clearfix() {
|
||||
*zoom:1;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content:"";
|
||||
display:table;
|
||||
}
|
||||
&:after {
|
||||
clear:both;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////
|
||||
// GRID //
|
||||
//////////
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
.clearfix;
|
||||
}
|
||||
|
||||
.row(@columns:@columns) {
|
||||
display: block;
|
||||
width: @total-width*((@gutter-width + @gridsystem-width)/@gridsystem-width);
|
||||
margin: 0 @total-width*(((@gutter-width*.5)/@gridsystem-width)*-1);
|
||||
// *width: @total-width*((@gutter-width + @gridsystem-width)/@gridsystem-width)-@correction;
|
||||
// *margin: 0 @total-width*(((@gutter-width*.5)/@gridsystem-width)*-1)-@correction;
|
||||
.clearfix;
|
||||
}
|
||||
.column(@x,@columns:@columns) {
|
||||
display: inline;
|
||||
float: left;
|
||||
width: @total-width*((((@gutter-width+@column-width)*@x)-@gutter-width) / @gridsystem-width);
|
||||
margin: 0 @total-width*((@gutter-width*.5)/@gridsystem-width);
|
||||
// *width: @total-width*((((@gutter-width+@column-width)*@x)-@gutter-width) / @gridsystem-width)-@correction;
|
||||
// *margin: 0 @total-width*((@gutter-width*.5)/@gridsystem-width)-@correction;
|
||||
}
|
||||
.push(@offset:1) {
|
||||
margin-left: @total-width*(((@gutter-width+@column-width)*@offset) / @gridsystem-width) + @total-width*((@gutter-width*.5)/@gridsystem-width);
|
||||
}
|
||||
.pull(@offset:1) {
|
||||
margin-right: @total-width*(((@gutter-width+@column-width)*@offset) / @gridsystem-width) + @total-width*((@gutter-width*.5)/@gridsystem-width);
|
||||
}
|
87
assets/css/master.less
Normal file
87
assets/css/master.less
Normal file
|
@ -0,0 +1,87 @@
|
|||
@import 'grid.import.less';
|
||||
@import 'var.import.less';
|
||||
@import 'fonts.import.less';
|
||||
@import 'rules.import.less';
|
||||
@import 'anim.import.less';
|
||||
|
||||
@import 'base.import.less';
|
||||
@import 'nav.import.less';
|
||||
|
||||
@import 'forms.import.less';
|
||||
@import 'tables.import.less';
|
||||
@import 'button.import.less';
|
||||
|
||||
@import 'media.import.less';
|
||||
|
||||
@import 'alert.import.less';
|
||||
|
||||
//ADD CUSTOM AND OVERRIDE CODE HERE
|
||||
|
||||
|
||||
|
||||
|
||||
//HOME
|
||||
|
||||
#mainlogo {
|
||||
background: url('/img/logo/Cycore_web_optimized.jpg')
|
||||
center center no-repeat;
|
||||
background-size: contain;
|
||||
height: 500px;
|
||||
}
|
||||
#mainstatement {
|
||||
color: @keydark;
|
||||
text-align: center;
|
||||
}
|
||||
#maincontact {
|
||||
h4 { text-align: center; }
|
||||
form { padding: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
input { margin: 2px; flex: 1 0 auto; }
|
||||
}
|
||||
#mainservice {
|
||||
padding: 0 10px;
|
||||
ul { .twelve;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
align-content: stretch;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
height: 5em;
|
||||
width: auto;
|
||||
margin: 10px;
|
||||
flex: 1 0 auto;
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
color: @darkergrey;
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
padding-top: 2em;
|
||||
.stripes;
|
||||
}
|
||||
.one {background-color: @contrast; }
|
||||
.two {background-color: lighten(#2196f3, 10%);}
|
||||
.three {background-color: fadeout(#4caf50, 10%);}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
14
assets/css/media.import.less
vendored
Normal file
14
assets/css/media.import.less
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
//COLUMN RESPONSE
|
||||
|
||||
|
||||
//add here
|
||||
|
||||
//ADD BREAKPOINTS IF NECESSARY
|
||||
|
||||
@media all and (max-width: 1180px) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
39
assets/css/nav.import.less
vendored
Normal file
39
assets/css/nav.import.less
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
|
||||
//NAV MENUS AND LINKS
|
||||
|
||||
.nav {
|
||||
position: fixed;
|
||||
height: 50px;
|
||||
margin: 0 0;
|
||||
padding: 0;
|
||||
z-index: 1000;
|
||||
background: black ;
|
||||
background-size: contain;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.logo { padding: 7px 0 0 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
padding-top: 18px;
|
||||
|
||||
.two;
|
||||
.slowtrans;
|
||||
z-index: 1000;
|
||||
|
||||
color: @keylight;
|
||||
|
||||
}
|
||||
a:hover {
|
||||
.fasttrans;
|
||||
}
|
||||
|
||||
|
||||
}
|
142
assets/css/rules.import.less
vendored
Normal file
142
assets/css/rules.import.less
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
.placeholder (@color) {
|
||||
input::-webkit-input-placeholder { color: @color; padding: 7px 10px 3px 10px;}
|
||||
input:-moz-placeholder { color: @color;padding: 7px 10px 3px 10px; }
|
||||
input::-moz-placeholder { color: @color;padding: 7px 10px 3px 10px; }
|
||||
input:-ms-input-placeholder { color: @color;padding: 7px 10px 3px 10px; }
|
||||
}
|
||||
.border-radius (@radius) {
|
||||
border-radius: @radius;
|
||||
-moz-border-radius: @radius;
|
||||
-webkit-border-radius: @radius;
|
||||
background-clip: padding-box;
|
||||
-moz-background-clip: padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
}
|
||||
.background-linear-gradient (@color1, @color2) {
|
||||
background: linear-gradient(top, @color1, @color2);
|
||||
background: -moz-linear-gradient(@color1, @color2); //FF3.6-
|
||||
background: -webkit-linear-gradient(top, @color1, @color2); //S5.1, Chrome 10-
|
||||
background: -o-linear-gradient(top, @color1, @color2); //O 11.1
|
||||
background: -ms-linear-gradient(@color1, @color2); //IE10
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@color1', endColorstr='@color2'); //IE6-7
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='@color1', endColorstr='@color2')"; //IE8-9
|
||||
}
|
||||
.background-linear-gradient (@color1, @color2, @color3) {
|
||||
background: linear-gradient(top, @color1, @color2, @color3);
|
||||
background: -moz-linear-gradient(@color1, @color2, @color3); //FF3.6-
|
||||
background: -webkit-linear-gradient(top, @color1, @color2, @color3); //S5.1, Chrome 10-
|
||||
background: -o-linear-gradient(top, @color1, @color2, @color3); //O 11.1
|
||||
background: -ms-linear-gradient(@color1, @color2, @color3); //IE10
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@color1', endColorstr='@color3'); //IE6-7
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='@color1', endColorstr='@color3')"; //IE8-9
|
||||
}
|
||||
.background-gradient (@degrees, @color1, @color2, @color3, @color4, @color5, @color6, @color7) {
|
||||
background-image: linear-gradient(@degrees, @color1, @color2, @color3, @color4, @color5, @color6, @color7);
|
||||
background-image: -moz-linear-gradient(@degrees, @color1, @color2, @color3, @color4, @color5, @color6, @color7);
|
||||
background-image: -webkit-linear-gradient(@degrees, @color1, @color2, @color3, @color4, @color5, @color6, @color7);
|
||||
background-image: -o-linear-gradient(@degrees, @color1, @color2, @color3, @color4, @color5, @color6, @color7);
|
||||
background-image: -ms-linear-gradient(@degrees, @color1, @color2, @color3, @color4, @color5, @color6, @color7);
|
||||
}
|
||||
.box-shadow (none) {
|
||||
box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
.box-shadow (@shadow) {
|
||||
box-shadow: @shadow;
|
||||
-moz-box-shadow: @shadow;
|
||||
-webkit-box-shadow: @shadow;
|
||||
}
|
||||
.box-shadow (@shadow1, @shadow2) {
|
||||
box-shadow: @shadow1, @shadow2;
|
||||
-moz-box-shadow: @shadow1, @shadow2;
|
||||
-webkit-box-shadow: @shadow1, @shadow2;
|
||||
}
|
||||
.box-shadow (@shadow1, @shadow2, @shadow3) {
|
||||
box-shadow: @shadow1, @shadow2, @shadow3;
|
||||
-moz-box-shadow: @shadow1, @shadow2, @shadow3;
|
||||
-webkit-box-shadow: @shadow1, @shadow2, @shadow3;
|
||||
}
|
||||
|
||||
.transition (@property, @duration, @function, @delay) {
|
||||
-webkit-transition: @property @duration @function @delay;
|
||||
-moz-transition: @property @duration @function @delay;
|
||||
-o-transition: @property @duration @function @delay;
|
||||
-ms-transition: @property @duration @function @delay;
|
||||
transition: @property @duration @function @delay;
|
||||
}
|
||||
.transition (@property1, @duration1, @function1, @delay1, @property2, @duration2, @function2, @delay2) {
|
||||
-webkit-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2;
|
||||
-moz-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2;
|
||||
-o-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2;
|
||||
-ms-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2;
|
||||
transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2;
|
||||
}
|
||||
.transition (@property1, @duration1, @function1, @delay1, @property2, @duration2, @function2, @delay2, @property3, @duration3, @function3, @delay3) {
|
||||
-webkit-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3;
|
||||
-moz-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3;
|
||||
-o-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3;
|
||||
-ms-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3;
|
||||
transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3;
|
||||
}
|
||||
.transition (@property1, @duration1, @function1, @delay1, @property2, @duration2, @function2, @delay2, @property3, @duration3, @function3, @delay3, @property4, @duration4, @function4, @delay4) {
|
||||
-webkit-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3, @property4 @duration4 @function4 @delay4;
|
||||
-moz-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3, @property4 @duration4 @function4 @delay4;
|
||||
-o-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3, @property4 @duration4 @function4 @delay4;
|
||||
-ms-transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3, @property4 @duration4 @function4 @delay4;
|
||||
transition: @property1 @duration1 @function1 @delay1, @property2 @duration2 @function2 @delay2, @property3 @duration3 @function3 @delay3, @property4 @duration4 @function4 @delay4;
|
||||
}
|
||||
|
||||
.transition (inherit) {
|
||||
-webkit-transition: inherit;
|
||||
-moz-transition: inherit;
|
||||
-o-transition: inherit;
|
||||
-ms-transition: inherit;
|
||||
transition: inherit;
|
||||
}
|
||||
.fasttrans { .transition(all, 0.4s, ease, 0s); }
|
||||
.slowtrans { .transition(all, 2s, ease, 0s); }
|
||||
|
||||
|
||||
.rotate (@deg) {
|
||||
-webkit-transform: rotate(@deg);
|
||||
-moz-transform: rotate(@deg);
|
||||
-o-transform: rotate(@deg);
|
||||
-ms-transform: rotate(@deg);
|
||||
transform: rotate(@deg);
|
||||
}
|
||||
|
||||
.scale(@amount) {
|
||||
-webkit-transform: scale(@amount);
|
||||
-moz-transform: scale(@amount);
|
||||
-ms-transform: scale(@amount);
|
||||
transform: scale(@amount);
|
||||
}
|
||||
.flipX {
|
||||
-webkit-transform: scaleX(-1);
|
||||
-moz-transform: scaleX(-1);
|
||||
-ms-transform: scaleX(-1);
|
||||
-o-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
//TEXTURES
|
||||
|
||||
.polka {
|
||||
background:
|
||||
radial-gradient(@lighttransblack 15%, transparent 16%) 0 0,
|
||||
radial-gradient(@lighttransblack 15%, transparent 16%) 8px 8px,
|
||||
radial-gradient(rgba(255,255,255,.01) 15%, transparent 20%) 0 1px,
|
||||
radial-gradient(rgba(255,255,255,.01) 15%, transparent 20%) 8px 9px;
|
||||
background-size: 8px 8px;
|
||||
}
|
||||
.stripes {
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
@transwhite 10px,
|
||||
@transwhite 20px
|
||||
);
|
||||
}
|
||||
|
49
assets/css/tables.import.less
vendored
Normal file
49
assets/css/tables.import.less
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
//TABLE
|
||||
|
||||
table {
|
||||
.unit;
|
||||
border-collapse: collapse;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
td, th {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 7px 5px;
|
||||
}
|
||||
|
||||
@color1: fadeout(#fff, 40%);
|
||||
@color2: fadeout(lighten(desaturate(@highlightblue, 20%), 40%), 45%);
|
||||
|
||||
tr:nth-child(2n+1) td {
|
||||
background: @color1;
|
||||
}
|
||||
tr:nth-child(2n) td {
|
||||
background: @color2;
|
||||
}
|
||||
|
||||
th {
|
||||
background: @lightergrey;
|
||||
color: @darkgrey;
|
||||
}
|
||||
tr:hover td:nth-child(1n) {
|
||||
background-color: @highlightyellow;
|
||||
}
|
||||
td.header {
|
||||
background: transparent !important;
|
||||
color: @darkergrey;
|
||||
text-shadow: 1px 1px 1px #fff;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
table i {
|
||||
color: @darkergrey;
|
||||
}
|
||||
table i:hover {
|
||||
color: fadeout(@lightlightblue, 30%);
|
||||
}
|
||||
|
||||
.large { width: 80%; margin-left: 10%; }
|
||||
|
54
assets/css/var.import.less
vendored
Normal file
54
assets/css/var.import.less
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
//COLOR VARIABLES
|
||||
|
||||
@background: white;
|
||||
@border: @transblack;
|
||||
|
||||
//BRAND COLORS
|
||||
|
||||
@key: #2d65af; //CHANGE ME!! NOT >>
|
||||
@keylight: #38b4e7;
|
||||
@keylightest: lighten(@key, 25%);
|
||||
@keydark: darken(@key, 15%);
|
||||
@keydarker: darken(@key, 29%);
|
||||
@keydarkest: darken(@key, 36%);
|
||||
|
||||
@contrast: #ff9900;
|
||||
@contrastdark: darken(@contrast, 28%);
|
||||
@contrastlight: lighten(@contrast, 15%);
|
||||
@contrastlightest: lighten(@contrast, 23%);
|
||||
|
||||
//FUNCTION COLORS
|
||||
|
||||
@lightlightblue: lighten(@highlightblue, 14%);
|
||||
@highlightblue: #0069ff;
|
||||
@darkhighlightblue: darken(@highlightblue, 10%);
|
||||
|
||||
@orange: #FF8300;
|
||||
@highlightyellow: #fcf4b5;
|
||||
@gold: #FFEF02;
|
||||
|
||||
@red: #aa0000;
|
||||
@purple: #1100ff;
|
||||
@green: #008d00;
|
||||
@darkgreen: darken(desaturate(@green, 10%), 10%);
|
||||
|
||||
//MONO
|
||||
|
||||
@lightestgrey: #eee;
|
||||
@lightergrey: #ddd;
|
||||
@lightgrey: #ccc;
|
||||
@grey: #aaa;
|
||||
@darkgrey: #888;
|
||||
@darkergrey: #555;
|
||||
@darkestgrey: #222;
|
||||
|
||||
@lighttransblack: rgba(0,0,0,0.02);
|
||||
@transblack: rgba(0, 0, 0, 0.2);
|
||||
@lightblack: rgba(0, 0, 0, 0.5);
|
||||
@midblack: rgba(0, 0, 0, 0.8);
|
||||
@transwhite: rgba(255, 255, 255, 0.05);
|
||||
@lightwhite: rgba(255, 255, 255, 0.1);
|
||||
@midwhite: rgba(255, 255, 255, 0.6);
|
||||
|
||||
|
||||
|
BIN
assets/img/favicon.png
Normal file
BIN
assets/img/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
BIN
assets/img/logo/Cycore_web_optimized.jpg
Normal file
BIN
assets/img/logo/Cycore_web_optimized.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
52
assets/js/home.js
Normal file
52
assets/js/home.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
function getCookie(name) {
|
||||
if (!document.cookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const xsrfCookies = document.cookie.split(';')
|
||||
.map(c => c.trim())
|
||||
.filter(c => c.startsWith(name + '='));
|
||||
|
||||
if (xsrfCookies.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return decodeURIComponent(xsrfCookies[0].split('=')[1]);
|
||||
}
|
||||
|
||||
function contactRequest() {
|
||||
var name = document.querySelector('input[name=name]').value
|
||||
var email = document.querySelector('input[name=email]').value
|
||||
var success = document.querySelector('div.alert-success')
|
||||
var failure = document.querySelector('div.alert-error')
|
||||
|
||||
// Reset response holders
|
||||
success.innerHTML = ''
|
||||
failure.innerHTML = ''
|
||||
|
||||
fetch("contact/request", {
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
email: email
|
||||
}),
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
'X-XSRF-TOKEN': getCookie('_csrf')
|
||||
}),
|
||||
method: 'POST'
|
||||
})
|
||||
.then(function(resp) {
|
||||
|
||||
if(resp.ok) {
|
||||
success.innerHTML = 'Request Sent'
|
||||
return
|
||||
}
|
||||
return resp.json()
|
||||
})
|
||||
.then(function(o) {
|
||||
failure.innerHTML = o.message
|
||||
})
|
||||
.catch( function(err) {
|
||||
failure.innerHTML = 'network error'
|
||||
})
|
||||
}
|
211
conf/app.conf
211
conf/app.conf
|
@ -1,211 +0,0 @@
|
|||
################################################################################
|
||||
# Revel configuration file
|
||||
# See:
|
||||
# http://revel.github.io/manual/appconf.html
|
||||
# for more detailed documentation.
|
||||
################################################################################
|
||||
|
||||
# This sets the `AppName` variable which can be used in your code as
|
||||
# `if revel.AppName {...}`
|
||||
app.name = cycore-web
|
||||
|
||||
# A secret string which is passed to cryptographically sign the cookie to prevent
|
||||
# (and detect) user modification.
|
||||
# Keep this string secret or users will be able to inject arbitrary cookie values
|
||||
# into your application
|
||||
app.secret = pggqixEuKBuN8nuwrUAkTyrjCnx5YtRNh4Y7TIHvmRKh0SXrqcqPxDny6UtMyxLL
|
||||
|
||||
# Revel running behind proxy like nginx, haproxy, etc
|
||||
app.behind.proxy = false
|
||||
|
||||
|
||||
# The IP address on which to listen.
|
||||
http.addr = 0.0.0.0
|
||||
|
||||
# The port on which to listen.
|
||||
http.port = 9000
|
||||
|
||||
# Whether to use SSL or not.
|
||||
http.ssl = false
|
||||
|
||||
# Path to an X509 certificate file, if using SSL.
|
||||
#http.sslcert =
|
||||
|
||||
# Path to an X509 certificate key, if using SSL.
|
||||
#http.sslkey =
|
||||
|
||||
|
||||
# For any cookies set by Revel (Session,Flash,Error) these properties will set
|
||||
# the fields of:
|
||||
# http://golang.org/pkg/net/http/#Cookie
|
||||
#
|
||||
# Each cookie set by Revel is prefixed with this string.
|
||||
cookie.prefix = CYCORE
|
||||
|
||||
# A secure cookie has the secure attribute enabled and is only used via HTTPS,
|
||||
# ensuring that the cookie is always encrypted when transmitting from client to
|
||||
# server. This makes the cookie less likely to be exposed to cookie theft via
|
||||
# eavesdropping.
|
||||
#
|
||||
# In dev mode, this will default to false, otherwise it will
|
||||
# default to true.
|
||||
# cookie.secure = false
|
||||
|
||||
# Limit cookie access to a given domain
|
||||
#cookie.domain =
|
||||
|
||||
# Define when your session cookie expires. Possible values:
|
||||
# "720h"
|
||||
# A time duration (http://golang.org/pkg/time/#ParseDuration) after which
|
||||
# the cookie expires and the session is invalid.
|
||||
# "session"
|
||||
# Sets a session cookie which invalidates the session when the user close
|
||||
# the browser.
|
||||
session.expires = 720h
|
||||
|
||||
|
||||
# The date format used by Revel. Possible formats defined by the Go `time`
|
||||
# package (http://golang.org/pkg/time/#Parse)
|
||||
format.date = 2006-01-02
|
||||
format.datetime = 2006-01-02 15:04
|
||||
|
||||
# Timeout specifies a time limit for request (in seconds) made by a single client.
|
||||
# A Timeout of zero means no timeout.
|
||||
timeout.read = 90
|
||||
timeout.write = 60
|
||||
|
||||
|
||||
# Determines whether the template rendering should use chunked encoding.
|
||||
# Chunked encoding can decrease the time to first byte on the client side by
|
||||
# sending data before the entire template has been fully rendered.
|
||||
results.chunked = false
|
||||
|
||||
|
||||
# Prefixes for each log message line
|
||||
# User can override these prefix values within any section
|
||||
# For e.g: [dev], [prod], etc
|
||||
log.trace.prefix = "TRACE "
|
||||
log.info.prefix = "INFO "
|
||||
log.warn.prefix = "WARN "
|
||||
log.error.prefix = "ERROR "
|
||||
|
||||
|
||||
# The default language of this application.
|
||||
i18n.default_language = en
|
||||
|
||||
# The default format when message is missing.
|
||||
# The original message shows in %s
|
||||
#i18n.unknown_format = "??? %s ???"
|
||||
|
||||
|
||||
# Module to serve static content such as CSS, JavaScript and Media files
|
||||
# Allows Routes like this:
|
||||
# `Static.ServeModule("modulename","public")`
|
||||
module.static=github.com/revel/modules/static
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
# Section: dev
|
||||
# This section is evaluated when running Revel in dev mode. Like so:
|
||||
# `revel run path/to/myapp`
|
||||
[dev]
|
||||
# This sets `DevMode` variable to `true` which can be used in your code as
|
||||
# `if revel.DevMode {...}`
|
||||
# or in your templates with
|
||||
# `<no value>`
|
||||
mode.dev = true
|
||||
|
||||
|
||||
# Pretty print JSON/XML when calling RenderJson/RenderXml
|
||||
results.pretty = true
|
||||
|
||||
|
||||
# Automatically watches your applicaton files and recompiles on-demand
|
||||
watch = true
|
||||
|
||||
|
||||
# If you set watch.mode = "eager", the server starts to recompile
|
||||
# your application every time your application's files change.
|
||||
watch.mode = "normal"
|
||||
|
||||
# Watch the entire $GOPATH for code changes. Default is false.
|
||||
#watch.gopath = true
|
||||
|
||||
|
||||
# Module to run code tests in the browser
|
||||
# See:
|
||||
# http://revel.github.io/manual/testing.html
|
||||
module.testrunner = github.com/revel/modules/testrunner
|
||||
|
||||
|
||||
# Where to log the various Revel logs
|
||||
log.trace.output = off
|
||||
log.info.output = stderr
|
||||
log.warn.output = stderr
|
||||
log.error.output = stderr
|
||||
|
||||
|
||||
# Revel log flags. Possible flags defined by the Go `log` package,
|
||||
# please refer https://golang.org/pkg/log/#pkg-constants
|
||||
# Go log is "Bits or'ed together to control what's printed"
|
||||
# Examples:
|
||||
# 0 => just log the message, turn off the flags
|
||||
# 3 => log.LstdFlags (log.Ldate|log.Ltime)
|
||||
# 19 => log.Ldate|log.Ltime|log.Lshortfile
|
||||
# 23 => log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile
|
||||
log.trace.flags = 19
|
||||
log.info.flags = 19
|
||||
log.warn.flags = 19
|
||||
log.error.flags = 19
|
||||
|
||||
|
||||
# Revel request access log
|
||||
# Access log line format:
|
||||
# RequestStartTime ClientIP ResponseStatus RequestLatency HTTPMethod URLPath
|
||||
# Sample format:
|
||||
# 2016/05/25 17:46:37.112 127.0.0.1 200 270.157µs GET /
|
||||
log.request.output = stderr
|
||||
|
||||
|
||||
################################################################################
|
||||
# Section: prod
|
||||
# This section is evaluated when running Revel in production mode. Like so:
|
||||
# `revel run path/to/myapp prod`
|
||||
# See:
|
||||
# [dev] section for documentation of the various settings
|
||||
[prod]
|
||||
mode.dev = false
|
||||
results.pretty = false
|
||||
watch = false
|
||||
module.testrunner =
|
||||
|
||||
log.trace.output = off
|
||||
log.info.output = off
|
||||
log.warn.output = stderr
|
||||
log.error.output = stderr
|
||||
|
||||
# Revel log flags. Possible flags defined by the Go `log` package,
|
||||
# please refer https://golang.org/pkg/log/#pkg-constants
|
||||
# Go log is "Bits or'ed together to control what's printed"
|
||||
# Examples:
|
||||
# 0 => just log the message, turn off the flags
|
||||
# 3 => log.LstdFlags (log.Ldate|log.Ltime)
|
||||
# 19 => log.Ldate|log.Ltime|log.Lshortfile
|
||||
# 23 => log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile
|
||||
log.trace.flags = 3
|
||||
log.info.flags = 3
|
||||
log.warn.flags = 3
|
||||
log.error.flags = 3
|
||||
|
||||
|
||||
# Revel request access log
|
||||
# Access log line format:
|
||||
# RequestStartTime ClientIP ResponseStatus RequestLatency HTTPMethod URLPath
|
||||
# Sample format:
|
||||
# 2016/05/25 17:46:37.112 127.0.0.1 200 270.157µs GET /
|
||||
# Example:
|
||||
# log.request.output = %(app.name)s-request.log
|
||||
log.request.output = off
|
||||
app.behind.proxy = true
|
||||
app.secret = ${COOKIE_SECRET}
|
22
conf/routes
22
conf/routes
|
@ -1,22 +0,0 @@
|
|||
# Routes
|
||||
# This file defines all application routes (Higher priority routes first)
|
||||
# ~~~~
|
||||
|
||||
module:testrunner
|
||||
|
||||
GET / App.Index
|
||||
|
||||
# Handle contact requests
|
||||
POST /contact/request Contact.ContactRequest
|
||||
|
||||
# Ignore favicon requests
|
||||
GET /favicon.ico 404
|
||||
|
||||
# Map static resources from the /app/public folder to the /public path
|
||||
GET /public/*filepath Static.Serve("public")
|
||||
|
||||
# GPG Public key
|
||||
GET /scm.asc Static.Serve("public", "scm.asc")
|
||||
|
||||
# Catch all
|
||||
* /:controller/:action :controller.:action
|
124
contact.go
Normal file
124
contact.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/CyCoreSystems/sendinblue"
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
var contactEmailT *template.Template
|
||||
|
||||
func init() {
|
||||
contactEmailT = template.Must(template.New("contactEmail").Parse(contactEmailTemplate))
|
||||
}
|
||||
|
||||
// ContactRequest describes the parameters of a contact request
|
||||
type ContactRequest struct {
|
||||
Name string `json:"name" form:"name" query:"name"`
|
||||
Email string `json:"email" form:"email" query:"email"`
|
||||
}
|
||||
|
||||
// contactRequest handles a customer contact request
|
||||
func contactRequest(c echo.Context) (err error) {
|
||||
|
||||
cc := c.(*Context)
|
||||
|
||||
req := new(ContactRequest)
|
||||
if err = cc.Bind(req); err != nil {
|
||||
cc.Log.Warnf("failed to parse input: %s", err.Error())
|
||||
return c.JSON(http.StatusBadRequest, NewError(errors.New("failed to read request")))
|
||||
}
|
||||
|
||||
cc.Log.Debugf(`received contact request from "%s" <%s> (%s)`, req.Name, req.Email, c.RealIP())
|
||||
|
||||
if req.Name == "" {
|
||||
cc.Log.Warn("empty name")
|
||||
return c.JSON(http.StatusBadRequest, NewError(errors.New("please supply a name")))
|
||||
}
|
||||
if req.Email == "" {
|
||||
cc.Log.Warn("empty email")
|
||||
return c.JSON(http.StatusBadRequest, NewError(errors.New("please supply an email")))
|
||||
}
|
||||
|
||||
emailBody, err := renderContactEmail(req.Name, req.Email)
|
||||
if err != nil {
|
||||
cc.Log.Errorf("failed to render email body: %s", err.Error())
|
||||
return c.JSON(http.StatusInternalServerError, NewError(errors.New("internal error; please retry")))
|
||||
}
|
||||
|
||||
msg := &sendinblue.Message{
|
||||
Sender: &sendinblue.Address{
|
||||
Name: "CyCore Systems, Inc",
|
||||
Email: "sys@cycoresys.com",
|
||||
},
|
||||
To: getEmailContacts(),
|
||||
Subject: "Contact Request",
|
||||
HTMLContent: emailBody,
|
||||
Tags: []string{"contact-request"},
|
||||
}
|
||||
|
||||
if err = msg.Send(os.Getenv("SENDINBLUE_APIKEY")); err != nil {
|
||||
cc.Log.Errorf("failed to send contact email: %s", err.Error())
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(msg)
|
||||
|
||||
return c.JSON(http.StatusBadGateway, NewError(errors.New("internal error; please retry")))
|
||||
}
|
||||
|
||||
cc.Log.Debug("contact request sent")
|
||||
return nil
|
||||
}
|
||||
|
||||
func renderContactEmail(name, email string) (string, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
err := contactEmailT.Execute(buf, struct {
|
||||
Name string
|
||||
Email string
|
||||
Timestamp string
|
||||
}{
|
||||
Name: name,
|
||||
Email: email,
|
||||
Timestamp: time.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func getEmailContacts() []*sendinblue.Address {
|
||||
|
||||
var ret []*sendinblue.Address
|
||||
if err := json.Unmarshal([]byte(os.Getenv("CONTACT_RECIPIENTS")), &ret); err != nil {
|
||||
|
||||
// Fall back to default if we fail to load from environment
|
||||
ret = append(ret, &sendinblue.Address{
|
||||
Name: "System Receiver",
|
||||
Email: "sys@cycoresys.com",
|
||||
})
|
||||
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var contactEmailTemplate = `
|
||||
<html>
|
||||
<body>
|
||||
<h3>Web Contact Form</h3>
|
||||
<ul>
|
||||
<li><b>Name:</b> {{.Name}}</li>
|
||||
<li><b>Email:</b> {{.Email}}</li>
|
||||
<li><b>Timestamp:</b> {{.Timestamp}}</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
`
|
18
context.go
Normal file
18
context.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/labstack/echo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Context is the custom context for this web server
|
||||
type Context struct {
|
||||
echo.Context
|
||||
|
||||
// DB is the database connection
|
||||
DB *sqlx.DB
|
||||
|
||||
// Log is the core logger
|
||||
Log *zap.SugaredLogger
|
||||
}
|
45
db/db.go
Normal file
45
db/db.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
_ "github.com/lib/pq" // load Postgresql driver here, since we have no access to main
|
||||
)
|
||||
|
||||
var db *sqlx.DB
|
||||
|
||||
// Connect attempts to establish a database connection
|
||||
func Connect() error {
|
||||
|
||||
if db != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var username = "nobody"
|
||||
|
||||
u, err := user.Current()
|
||||
if err == nil {
|
||||
username = u.Username
|
||||
}
|
||||
|
||||
dsn := fmt.Sprintf("postgresql://%s@localhost:26257/cycore?sslmode=disable", username)
|
||||
if os.Getenv("DSN") != "" {
|
||||
dsn = os.Getenv("DSN")
|
||||
}
|
||||
|
||||
db, err = sqlx.Open("postgres", dsn)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Get returns a database connection handle for the database
|
||||
func Get() *sqlx.DB {
|
||||
if err := Connect(); err != nil {
|
||||
panic("no database connection: " + err.Error())
|
||||
}
|
||||
return db
|
||||
}
|
4
dev.sh
Executable file
4
dev.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
go generate
|
||||
go build
|
||||
./cycore-web -debug
|
100
main.go
Normal file
100
main.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package main
|
||||
|
||||
//go:generate esc -o static.go -prefix assets -ignore \.map$ assets
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/CyCoreSystems/cycore-web/db"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var addr string
|
||||
var debug bool
|
||||
|
||||
// Error indicates an error from processing
|
||||
type Error struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// NewError converts a standard error to an error response
|
||||
func NewError(err error) *Error {
|
||||
return &Error{
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&addr, "addr", ":9000", "listen address")
|
||||
flag.BoolVar(&debug, "debug", false, "run with debug logging")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
var err error
|
||||
|
||||
var logger *zap.Logger
|
||||
if debug {
|
||||
logger, err = zap.NewDevelopment()
|
||||
} else {
|
||||
logger, err = zap.NewProduction()
|
||||
}
|
||||
if err != nil {
|
||||
panic("failed to create logger: " + err.Error())
|
||||
}
|
||||
defer logger.Sync() // nolint
|
||||
|
||||
log := logger.Sugar()
|
||||
|
||||
err = db.Connect()
|
||||
if err != nil {
|
||||
log.Panicf("failed to open database: %v", err)
|
||||
}
|
||||
defer db.Get().Close() // nolint
|
||||
|
||||
e := echo.New()
|
||||
//e.Use(middleware.CSRF())
|
||||
e.Use(middleware.Gzip())
|
||||
e.Use(middleware.Logger())
|
||||
e.Use(middleware.Recover())
|
||||
//e.Use(middleware.Secure())
|
||||
|
||||
// Create custom context
|
||||
e.Use(func(h echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
cc := &Context{
|
||||
Context: c,
|
||||
DB: db.Get(),
|
||||
Log: log,
|
||||
}
|
||||
return h(cc)
|
||||
}
|
||||
})
|
||||
|
||||
e.Renderer = &Template{
|
||||
templates: template.Must(template.ParseGlob("views/*.html")),
|
||||
}
|
||||
|
||||
assets := http.FileServer(FS(debug))
|
||||
|
||||
e.GET("/css/*", echo.WrapHandler(assets))
|
||||
e.GET("/img/*", echo.WrapHandler(assets))
|
||||
e.GET("/js/*", echo.WrapHandler(assets))
|
||||
e.GET("/scm.asc", echo.WrapHandler(assets))
|
||||
|
||||
e.GET("/", home)
|
||||
|
||||
e.POST("/contact/request", contactRequest)
|
||||
|
||||
log.Fatal(e.Start(addr))
|
||||
}
|
||||
|
||||
func home(c echo.Context) error {
|
||||
return c.Render(http.StatusOK, "index.html", nil)
|
||||
}
|
18
templates.go
Normal file
18
templates.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
// Template implements echo.Renderer
|
||||
type Template struct {
|
||||
templates *template.Template
|
||||
}
|
||||
|
||||
// Render executes the selected template with the given data
|
||||
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
return t.templates.ExecuteTemplate(w, name, data)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package tests
|
||||
|
||||
import "github.com/revel/revel/testing"
|
||||
|
||||
type AppTest struct {
|
||||
testing.TestSuite
|
||||
}
|
||||
|
||||
func (t *AppTest) Before() {
|
||||
println("Set up")
|
||||
}
|
||||
|
||||
func (t *AppTest) TestThatIndexPageWorks() {
|
||||
t.Get("/")
|
||||
t.AssertOk()
|
||||
t.AssertContentType("text/html; charset=utf-8")
|
||||
}
|
||||
|
||||
func (t *AppTest) After() {
|
||||
println("Tear down")
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<h4>Set up a no-obligation fifteen minute call now
|
||||
<form action="/contact/request" method="POST">
|
||||
<form action="#" onsubmit="contactRequest()">
|
||||
<input type="text" name="name" placeholder="Your Name" REQUIRED />
|
||||
<input type="email" name="email" placeholder="Your Email" REQUIRED />
|
||||
<input type="submit" name="submit" value="Let's Chat" />
|
12
views/footer.html
Normal file
12
views/footer.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
<div class="footer">
|
||||
<div class="credit">
|
||||
<a href="http://www.cycoresys.com">Site by CyCore Systems</a>
|
||||
</div>
|
||||
<div class="copy">
|
||||
© 2018 CyCore Systems
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -2,18 +2,18 @@
|
|||
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.title}}</title>
|
||||
<title>CyCore Systems, Inc</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="author" content="CyCore Systems, Inc.">
|
||||
<link rel="shortcut icon" type="image/png" href="/public/img/favicon.png">
|
||||
<link rel="stylesheet/less" type="text/css" href="/public/css/master.less"/>
|
||||
<link rel="shortcut icon" type="image/png" href="/img/favicon.png">
|
||||
<link rel="stylesheet/less" type="text/css" href="/css/master.less"/>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/2.7.1/less.min.js"></script>
|
||||
{{range .moreStyles}}
|
||||
<link rel="stylesheet" type="text/css" href="/public/{{.}}">
|
||||
{{end}}
|
||||
{{range .moreScripts}}
|
||||
<script src="/public/{{.}}" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="/js/{{.}}" type="text/javascript" charset="utf-8"></script>
|
||||
{{end}}
|
||||
</head>
|
||||
<body>
|
|
@ -1,4 +1,3 @@
|
|||
{{set . "title" "CyCore Systems"}}
|
||||
{{template "header.html" .}}
|
||||
|
||||
<div id="page">
|
||||
|
@ -11,7 +10,9 @@
|
|||
</h2>
|
||||
</div>
|
||||
<div class="transcontentunit twelve" id="maincontact">
|
||||
{{template "flash.html" .}}
|
||||
<div class="alert alert-success"></div>
|
||||
<div class="alert alert-error"></div>
|
||||
|
||||
{{template "contact.html" .}}
|
||||
</div>
|
||||
<div class="transcontentunit twelve" id="mainservice">
|
||||
|
@ -28,4 +29,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/home.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
{{template "footer.html" .}}
|
Loading…
Reference in a new issue