Bladeren bron

reorg and logon handler

bmallred 9 jaren geleden
bovenliggende
commit
ddc9ef4599
16 gewijzigde bestanden met toevoegingen van 257 en 315 verwijderingen
  1. 6 2
      account.go
  2. 7 5
      account_test.go
  3. 3 3
      environment.go
  4. 1 1
      environment_test.go
  5. 26 0
      handlers/logon.go
  6. 1 1
      html/article.html
  7. 6 13
      html/blog.html
  8. 179 259
      html/default.html
  9. 6 13
      html/loop.html
  10. 2 2
      html/workspace.html
  11. BIN
      loop
  12. 8 8
      main.go
  13. 1 1
      markdown.go
  14. 1 1
      markdown_test.go
  15. 6 4
      security.go
  16. 4 2
      security_test.go

+ 6 - 2
account.go

1
package main
1
package account
2
2
3
import (
3
import (
4
	"encoding/json"
4
	"encoding/json"
9
	"path/filepath"
9
	"path/filepath"
10
	"strings"
10
	"strings"
11
	"time"
11
	"time"
12
13
	"code.revolvingcow.com/revolvingcow/loop/environment"
14
	"code.revolvingcow.com/revolvingcow/loop/security"
12
)
15
)
13
16
14
const (
17
const (
18
	UserFile          = "loop_users"
15
	publishDateLayout = "200601021504"
19
	publishDateLayout = "200601021504"
16
)
20
)
17
21
200
204
201
	// Create new content directory if necessary
205
	// Create new content directory if necessary
202
	if content == "" {
206
	if content == "" {
203
		content = fmt.Sprintf("%x", hash(userDir+time.Now().Format(publishDateLayout), salt()))
207
		content = fmt.Sprintf("%x", security.Hash(userDir+time.Now().Format(publishDateLayout), environment.Salt()))
204
	}
208
	}
205
209
206
	// Create new content directory
210
	// Create new content directory

+ 7 - 5
account_test.go

1
package main
1
package account
2
2
3
import (
3
import (
4
	"os"
4
	"os"
5
	"testing"
5
	"testing"
6
	"time"
6
	"time"
7
8
	"code.revolvingcow.com/revolvingcow/loop/environment"
7
)
9
)
8
10
9
func TestGetAccounts(t *testing.T) {
11
func TestGetAccounts(t *testing.T) {
19
		Passphrase: "guest",
21
		Passphrase: "guest",
20
	}
22
	}
21
23
22
	ConfigureEnvironment()
24
	environment.ConfigureEnvironment()
23
	err := a.Create()
25
	err := a.Create()
24
	if err != nil {
26
	if err != nil {
25
		t.Error(err)
27
		t.Error(err)
55
		Passphrase: "guest",
57
		Passphrase: "guest",
56
	}
58
	}
57
59
58
	ConfigureEnvironment()
60
	environment.ConfigureEnvironment()
59
	fp, err := a.Add("", "", []byte{})
61
	fp, err := a.Add("", "", []byte{})
60
	if err == nil {
62
	if err == nil {
61
		t.Error("Fake content should not be publishable")
63
		t.Error("Fake content should not be publishable")
76
		Passphrase: "guest",
78
		Passphrase: "guest",
77
	}
79
	}
78
80
79
	ConfigureEnvironment()
81
	environment.ConfigureEnvironment()
80
	err := a.Publish("My first blog post", "thisshouldnotexist", time.Now())
82
	err := a.Publish("My first blog post", "thisshouldnotexist", time.Now())
81
	if err == nil {
83
	if err == nil {
82
		t.Error("Fake content should not be publishable")
84
		t.Error("Fake content should not be publishable")
89
		Passphrase: "guest",
91
		Passphrase: "guest",
90
	}
92
	}
91
93
92
	ConfigureEnvironment()
94
	environment.ConfigureEnvironment()
93
	err := a.Delete()
95
	err := a.Delete()
94
	if err != nil {
96
	if err != nil {
95
		t.Error(err)
97
		t.Error(err)

+ 3 - 3
environment.go

1
package main
1
package environment
2
2
3
import "os"
3
import "os"
4
4
19
	}
19
	}
20
}
20
}
21
21
22
func address() string {
22
func Address() string {
23
	return os.Getenv("LOOP_URL")
23
	return os.Getenv("LOOP_URL")
24
}
24
}
25
25
26
func salt() string {
26
func Salt() string {
27
	return os.Getenv("LOOP_SALT")
27
	return os.Getenv("LOOP_SALT")
28
}
28
}

+ 1 - 1
environment_test.go

1
package main
1
package environment
2
2
3
import (
3
import (
4
	"os"
4
	"os"

+ 26 - 0
handlers/logon.go

1
package handlers
2
3
import (
4
	"net/http"
5
6
	"code.revolvingcow.com/revolvingcow/loop/account"
7
)
8
9
func LogonHandler(w http.ResponseWriter, r *http.Request) {
10
	email := r.FormValue("email")
11
	password := r.FormValue("password")
12
	a := account.Account{
13
		Username:   email,
14
		Passphrase: password,
15
	}
16
17
	err := a.Create()
18
	if err != nil {
19
		if err.Error() == "Account already exists" {
20
			http.Error(w, err.Error(), http.StatusInternalServerError)
21
			return
22
		}
23
	}
24
25
	http.Redirect(w, r, "/"+a.Username, http.StatusSeeOther)
26
}

+ 1 - 1
html/article.html

1
{{ define "title" }}{{ end }}
1
{{ define "title" }}{{ end }}
2
{{ define "style" }}{{ end }}
2
{{ define "styles" }}{{ end }}
3
{{ define "content" }}
3
{{ define "content" }}
4
    <article>
4
    <article>
5
        <h3>
5
        <h3>

+ 6 - 13
html/blog.html

3
<head>
3
<head>
4
    <meta charset="utf-8">
4
    <meta charset="utf-8">
5
    <meta http-equiv="X-UC-Compatible" content="IE=edge">
5
    <meta http-equiv="X-UC-Compatible" content="IE=edge">
6
    <meta name="viewport" content="width=device-width, initial-scale=1">
6
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
7
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
7
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
8
8
9
    <title>{{ template "title" . }}</title>
9
    <title>{{ template "title" . }}</title>
10
10
11
    <link rel="shortcut icon" type=image/png" href="">
12
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
13
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">
14
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css">
15
    <link href="http://fonts.googleapis.com/css?family=Raleway:700,400" rel="stylesheet" type="text/css">
11
    <link rel="shortcut icon" type="image/png" href="">
12
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.95.3/css/materialize.min.css">
16
    {{ template "styles" . }}
13
    {{ template "styles" . }}
17
18
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
19
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
20
    <!--[if lt IE 9]>
21
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
22
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
23
    <![endif]-->
24
</head>
14
</head>
25
<body>
15
<body>
26
    {{ template "content" . }}
16
    {{ template "content" . }}
17
18
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
19
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.95.3/js/materialize.min.js"></script>
27
    {{ template "scripts" . }}
20
    {{ template "scripts" . }}
28
</body>
21
</body>
29
</html>
22
</html>

+ 179 - 259
html/default.html

1
{{ define "title" }}Loop{{ end }}
1
{{ define "title" }}Welcome to Loop!{{ end }}
2
{{ define "styles" }}
2
{{ define "styles" }}
3
    <style type="text/css">
3
    <style type="text/css">
4
        /* Parallax base styles
5
        --------------------------------------------- */
6
7
        .parallax {
8
            height: 500px; /* fallback for older browsers */
9
            height: 100vh;
10
            overflow-x: hidden;
11
            overflow-y: auto;
12
            -webkit-perspective: 300px;
13
                    perspective: 300px;
14
            -webkit-perspective-origin-x: 100%;
15
                    perspective-origin-x: 100%;
16
        }
17
18
        .parallax-group {
19
            position: relative;
20
            height: 500px; /* fallback for older browsers */
21
            height: 100vh;
22
            -webkit-transform-style: preserve-3d;
23
                    transform-style: preserve-3d;
24
        }
25
26
        .parallax-layer {
27
            position: absolute;
28
            top: 0;
29
            left: 0;
30
            right: 0;
31
            bottom: 0;
32
            -webkit-transform-origin-x: 100%;
33
                    transform-origin-x: 100%;
34
        }
35
36
        .parallax-layer-fore {
37
            -webkit-transform: translateZ(90px) scale(.7);
38
                    transform: translateZ(90px) scale(.7);
39
            z-index: 1;
40
        }
41
42
        .parallax-layer-base {
43
            -webkit-transform: translateZ(0);
44
                    transform: translateZ(0);
45
            z-index: 4;
46
        }
47
48
        .parallax-layer-back {
49
            -webkit-transform: translateZ(-300px) scale(2);
50
                    transform: translateZ(-300px) scale(2);
51
            z-index: 3;
52
        }
53
54
        .parallax-layer-deep {
55
            -webkit-transform: translateZ(-600px) scale(3);
56
                    transform: translateZ(-600px) scale(3);
57
            z-index: 2;
58
        }
59
60
        /* Debugger styles - used to show the effect
61
        --------------------------------------------- */
62
63
        .debug {
64
            position: fixed;
65
            bottom: 0;
66
            right: 0;
67
            z-index: 999;
68
            background: rgba(0,0,0,.85);
69
            color: #fff;
70
            padding: .5em;
71
            /*border-radius: 0 0 5px 5px;*/
72
        }
73
        .debug-on .parallax-group {
74
            -webkit-transform: translate3d(800px, 0, -800px) rotateY(30deg);
75
                    transform: translate3d(700px, 0, -800px) rotateY(30deg);
76
        }
77
        .debug-on .parallax-layer {
78
            box-shadow: 0 0 0 2px #000;
79
            opacity: 0.9;
80
        }
81
        .parallax-group {
82
            -webkit-transition: -webkit-transform 0.5s;
83
                    transition: transform 0.5s;
84
        }
85
86
        /* demo styles
87
        --------------------------------------------- */
88
89
        body, html {
90
            overflow: hidden;
91
        }
92
93
        body {
94
            font: 100% / 1.5 Arial;
95
        }
96
97
        * {
98
            margin:0;
99
            padding:0;
100
        }
101
102
        .parallax {
103
            font-size: 200%;
104
            background: rgb(255, 249, 242);
105
        }
106
107
        /* centre the content in the parallax layers */
108
        .title {
109
            text-align: center;
110
            position: absolute;
111
            left: 50%;
112
            top: 50%;
113
            -webkit-transform: translate(-50%, -50%);
114
                    transform: translate(-50%, -50%);
4
        nav ul a,
5
        nav .brand-logo {
6
            color: #444;
115
        }
7
        }
116
8
117
        /* style the groups
118
        --------------------------------------------- */
119
120
        #group1 {
121
            z-index: 5; /* slide over group 2 */
122
        }
123
        #group1 .parallax-layer-base {
124
            background: rgb(255, 249, 242);
9
        p {
10
            line-height: 2rem;
125
        }
11
        }
126
12
127
        #group2 {
128
            z-index: 3; /* slide under groups 1 and 3 */
129
        }
130
        #group2 .parallax-layer-back {
131
            background: rgb(0, 0, 0);
132
            color: rgb(255, 249, 242);
13
        .button-collapse {
14
            color: #26a69a;
133
        }
15
        }
134
16
135
        #group3 {
136
            z-index: 4; /* slide over group 2 and 4 */
137
        }
138
        #group3 .parallax-layer-base {
139
            background: rgb(255, 249, 242);
17
        .parallax-container {
18
            min-height: 380px;
19
            line-height: 0;
20
            height: auto;
21
            color: rgba(255, 255, 255, 0.9);
140
        }
22
        }
141
23
142
        #group4 {
143
            z-index: 2; /* slide under group 3 and 5 */
144
        }
145
        #group4 .parallax-layer-deep {
146
            background: rgb(184, 223, 101);
24
        .parallax-container .section {
25
            width: 100%;
147
        }
26
        }
148
27
149
        #group5 {
150
            z-index: 3; /* slide over group 4 and 6 */
151
        }
152
        #group5 .parallax-layer-base {
153
            background: rgb(214, 229, 100);
28
        @media only screen and (max-width: 992px) {
29
            .parallax-container .section {
30
                position: absolute;
31
                    top: 40%;
32
            }
33
            #index-banner .section {
34
                top: 10%;
35
            }
154
        }
36
        }
155
37
156
        #group6 {
157
            z-index: 2; /* slide under group 5 and 7 */
158
        }
159
        #group6 .parallax-layer-back {
160
            background: rgb(245, 235, 100);
38
        @media only screen and (max-width: 600px) {
39
            #index-banner .section {
40
                top: 0;
41
            }
161
        }
42
        }
162
43
163
        #group7 {
164
            z-index: 3; /* slide over group 7 */
44
        .icon-block {
45
            padding: 0 15px;
165
        }
46
        }
166
        #group7 .parallax-layer-base {
167
            background: rgb(255, 241, 100);
168
        }
169
170
47
171
        /* misc
172
        --------------------------------------------- */
173
        .demo__info {
174
            position: absolute;
175
            z-index:100;
176
            bottom: 1vh;
177
            top: auto;
178
            font-size:80%;
179
            text-align:center;
180
            width: 100%;
181
        }
182
183
        .fa {
184
            max-width: 55px;
48
        footer.page-footer {
49
            margin: 0;
185
        }
50
        }
186
    </style>
51
    </style>
187
{{end}}
52
{{ end }}
188
{{ define "content" }}
53
{{ define "content" }}
189
    <div class="parallax">
190
        <div id="group1" class="parallax-group">
191
            <div class="parallax-layer parallax-layer-base">
192
                <div class="col-sm-6 text-center">
193
                    <img src="loop.png" alt="loop" class="img-responsive img-circle" style="display: inline;" />
54
    <nav class="white" role="navigation">
55
        <div class="nav-wrapper container">
56
            <a id="logo-container" href="#" class="brand-logo">Loop</a>
57
            <ul class="right">
58
                <li><a href="#logon">Log On</a></li>
59
            </ul>
60
            <ul id="nav-mobile" class="side-nav">
61
                <li><a href="#logon">Log On</a></li>
62
            </ul>
63
            <a href="#" data-activates="nav-mobile" class="button-collapse"><i class="mdi-navigation-menu"></i></a>
64
        </div>
65
    </nav>
66
67
    <div id="index-banner" class="parallax-container">
68
        <div class="section no-pad-bot">
69
            <div class="container">
70
                <br><br>
71
                <h1 class="header center teal-text text-lighten-2">Loop</h1>
72
                <div class="row center">
73
                    <h5 class="header col s12 black-text light">A modern platform aiming to tighten the feedback loop</h5>
74
                </div>
75
                <div class="row center">
76
                    <a href="#logon" id="download-button" class="btn-large waves-effect waves-light teal lighten-1">Get Started</a>
194
                </div>
77
                </div>
195
                <div class="col-sm-5" style="padding-top: 5em;">
196
                    <h1>Loop</h1>
197
                    <p>Shortening the feedback loop in business and life.</p>
78
                <br><br>
79
            </div>
80
        </div>
81
        <div class="parallax">
82
            <img src="https://ununsplash.imgix.net/uploads/1413387158190559d80f7/6108b580" alt="Charles Forerunner">
83
        </div>
84
    </div>
198
85
199
                    <form action="" method="post" class="form form-horizontal">
200
                        <div class="form-group">
201
                            <div class="col-sm-6">
202
                                <input type="text" id="username" name="u" class="form-control" placeholder="Username" required autofocus />
86
    <div class="container">
87
        <div class="section">
88
            <div class="row">
89
                <div class="col s12 m4">
90
                    <div class="icon-block">
91
                        <h2 class="center brown-text"><i class="mdi-image-flash-on"></i></h2>
92
                        <h5 class="center">Speeds up development</h5>
93
                        <p class="light">
94
                            We did most of the heavy lifting for you to provide a default stylings that incorporate
95
                            our custom components. Additionally, we refined animations and transitions to provide a
96
                            smoother experience for developers.
97
                        </p>
98
                    </div>
99
                </div>
100
                <div class="col s12 m4">
101
                    <div class="icon-block">
102
                        <h2 class="center brown-text"><i class="mdi-social-group"></i></h2>
103
                        <h5 class="center">User Experience Focused</h5>
104
                        <p class="light">
105
                            By utilizing elements and principles of Material Design, we were able to create a
106
                            framework that incorporates components and animations that provide more feedback to
107
                            users. Additionally, a single underlying responsive system across all platforms allow
108
                            for a more unified user experience.
109
                        </p>
110
                    </div>
111
                </div>
112
                <div class="col s12 m4">
113
                    <div class="icon-block">
114
                        <h2 class="center brown-text"><i class="mdi-communication-forum"></i></h2>
115
                        <h5 class="center">Easy to collaborate</h5>
116
                        <p class="light">
117
                            We have provided detailed documentation as well as specific code examples to help new
118
                            users get started. We are also always open to feedback and can answer any questions a
119
                            user may have about Materialize.
120
                        </p>
121
                    </div>
122
                </div>
123
            </div>
124
        </div>
125
    </div>
126
127
    <div class="parallax-container valign-wrapper">
128
        <div class="section no-pad-bot">
129
            <div class="container">
130
                <div class="row center">
131
                    <h5 class="header col s12 light">A modern responsive front-end framework based on Material Design</h5>
132
                </div>
133
            </div>
134
        </div>
135
        <div class="parallax">
136
            <img src="https://unsplash.imgix.net/45/QDSMoAMTYaZoXpcwBjsL__DSC0104-1.jpg" alt="Aleksi Tappura">
137
        </div>
138
    </div>
139
140
    <div class="container">
141
        <div class="section">
142
            <div class="row">
143
                <div class="col l6 offset-l3 s12 center">
144
                    <a id="logon"></a>
145
                    <h3><i class="mdi-content-send brown-text"></i></h3>
146
                    <h4>Give it a try!</h4>
147
                    <p class="left-align light">
148
                        It is free to use
149
                    </p>
150
                    <form class="col s12" method="post" action="/logon">
151
                        <div class="row">
152
                            <div class="input-field col s12">
153
                                <input id="email" type="text" class="validate">
154
                                <label for="email">Email</label>
203
                            </div>
155
                            </div>
204
                            <div class="col-sm-6">
205
                                <input type="password" id="password" name="p" class="form-control" placeholder="Password" required />
156
                            <div class="input-field col s12">
157
                                <input id="password" type="password" class="validate">
158
                                <label for="password">Password</label>
206
                            </div>
159
                            </div>
207
                        </div>
208
                        <div class="form-group">
209
                            <div class="col-sm-offset-8 col-sm-4">
210
                                <button class="btn btn-lg btn-primary btn-block" type="submit" title="Sign in">Sign in</button>
160
                            <div class="input-field col s12">
161
                                <button class="btn waves-effect waves-light" type="submit" name="action">
162
                                    Submit
163
                                    <i class="mdi-content-send right"></i>
164
                                </button>
211
                            </div>
165
                            </div>
212
                        </div>
166
                        </div>
213
                    </form>
167
                    </form>
214
                </div>
168
                </div>
215
                <div class="col-sm-1">&nbsp;</div>
216
            </div>
169
            </div>
217
        </div>
170
        </div>
218
        <div id="group2" class="parallax-group">
219
            <div class="parallax-layer parallax-layer-base">
220
            </div>
221
            <div class="parallax-layer parallax-layer-back">
222
                <div class="title">Features</div>
171
    </div>
172
173
    <div class="parallax-container valign-wrapper">
174
        <div class="section no-pad-bot">
175
            <div class="container">
176
                <div class="row center">
177
                    <h5 class="header col s12 light">A modern responsive front-end framework based on Material Design</h5>
178
                </div>
223
            </div>
179
            </div>
224
        </div>
180
        </div>
225
        <div id="group3" class="parallax-group">
226
            <div class="parallax-layer parallax-layer-base">
227
                <div class="container" style="padding-top: 5em;">
228
                    <div class="row">
229
                        <div class="col-sm-6">
230
                            <i class="pull-left fa fa-3x fa-cloud-download"></i>
231
                            <h3>Easy to install</h3>
232
                            <p>Compiled to a single binary deploying first-time installs and updates is incredibly simple.</p>
233
                        </div>
234
                        <div class="col-sm-6">
235
                            <i class="pull-left fa fa-3x fa-desktop"></i>
236
                            <h3>Cross-platform</h3>
237
                            <p>Loop runs everywhere! Just download the appropriate binary for one of the following platforms: Linux, Mac OS X, or Windows.</p>
238
                        </div>
239
                    </div>
240
                    <div class="row">
241
                        <div class="col-sm-6">
242
                            <i class="pull-left fa fa-3x fa-paper-plane"></i>
243
                            <h3>Lightweight</h3>
244
                            <p>No additional databases, dependencies on external resources, and on-demand processing keeps minimum requirements low.</p>
245
                        </div>
246
                        <div class="col-sm-6">
247
                            <i class="pull-left fa fa-3x fa-code"></i>
248
                            <h3>Open Source</h3>
249
                            <p>The project is licensed under the GPLv2 and can be tailored to suit your needs.</p>
250
                        </div>
251
                    </div>
252
                    <div class="row">
253
                        <div class="col-sm-6">
254
                            <i class="pull-left fa fa-3x fa-edit"></i>
255
                            <h3>Blogging</h3>
256
                            <p>Whether it is a simple note, todo list, or marketing for your business blogging is made simple by taking advantage of the simplicity of Markdown and ease of publishing.</p>
257
                        </div>
258
                        <div class="col-sm-6">
259
                            <i class="pull-left fa fa-3x fa-line-chart"></i>
260
                            <h3>Metrics</h3>
261
                            <p>Without understanding where you have started you cannot accurately gauge where you have gone. This is why metrics are stored with every action within the system.</p>
262
                        </div>
263
                    </div>
264
                    <div class="row">
265
                        <div class="col-sm-6">
266
                            <i class="pull-left fa fa-3x fa-credit-card"></i>
267
                            <h3>Payments</h3>
268
                            <p>Internal invoicing has never been so easy with built-in handling to Stripe.</p>
269
                        </div>
270
                        <div class="col-sm-6">
271
                            <i class="pull-left fa fa-3x fa-archive"></i>
272
                            <h3>Data storage</h3>
273
                            <p>Containers can store any file type making it simple to organize and store data privately in the cloud.</p>
274
                        </div>
275
                    </div>
181
        <div class="parallax">
182
            <img src="https://unsplash.imgix.net/uploads/1411724908903377d4696/2e9b0cb2" alt="Sergei Zolkin">
183
        </div>
184
    </div>
185
186
    <footer class="page-footer teal">
187
        <div class="container">
188
            <div class="row">
189
                <div class="col l9 s12">
190
                    <h5 class="white-text">Who are we?</h5>
191
                    <p class="grey-text text-lighten-4">
192
                        We are a team of developers constantly refining our internal processes to bring all parties
193
                        together during the project life cycle.
194
                    </p>
195
                </div>
196
                <div class="col l3 s12">
197
                    <h5 class="white-text">Connect</h5>
198
                    <ul>
199
                        <li><a class="white-text" href="https://revolvingcow.com">Company Site</a></li>
200
                        <li><a class="white-text" href="https://code.revolvingcow.com/revolvingcow/loop">Source Code</a></li>
201
                        <li><a class="white-text" href="mailto:info@revolvingcow.com">Email</a></li>
202
                    </ul>
276
                </div>
203
                </div>
277
            </div>
204
            </div>
278
            <div class="parallax-layer parallax-layer-back"></div>
279
        </div>
205
        </div>
280
        <div id="group4" class="parallax-group">
281
            <div class="parallax-layer parallax-layer-base"></div>
282
            <div class="parallax-layer parallax-layer-deep">
206
        <div class="footer-copyright">
207
            <div class="container">
208
                Made by <a class="brown-text text-lighten-3" href="https://revolvingcow.com">Revolving Cow, LLC</a>
283
            </div>
209
            </div>
284
        </div>
210
        </div>
285
    </div>
286
287
    <div class="debug">
288
        <label>
289
            <input id="debugToggle" type="checkbox" />Debug
290
        </label>
291
    </div>
211
    </footer>
292
{{ end }}
212
{{ end }}
293
{{ define "scripts" }}
213
{{ define "scripts" }}
294
    <script type="text/javascript">
214
    <script type="text/javascript">
295
        var debugInput = document.getElementById("debugToggle");
296
        function updateDebugState() {
297
            document.body.classList.toggle('debug-on', debugInput.checked);
298
        }
299
        debugInput.addEventListener("click", updateDebugState);
300
        updateDebugState();
215
        (function ($) {
216
            $(function () {
217
                $('.button-collapse').sideNav();
218
                $('.parallax').parallax();
219
            });
220
        })(jQuery);
301
    </script>
221
    </script>
302
{{ end }}
222
{{ end }}

+ 6 - 13
html/loop.html

3
<head>
3
<head>
4
    <meta charset="utf-8">
4
    <meta charset="utf-8">
5
    <meta http-equiv="X-UC-Compatible" content="IE=edge">
5
    <meta http-equiv="X-UC-Compatible" content="IE=edge">
6
    <meta name="viewport" content="width=device-width, initial-scale=1">
6
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
7
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
7
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
8
8
9
    <title>{{ template "title" . }}</title>
9
    <title>{{ template "title" . }}</title>
10
10
11
    <link rel="shortcut icon" type=image/png" href="">
12
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
13
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">
14
    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css">
15
    <link href="http://fonts.googleapis.com/css?family=Raleway:700,400" rel="stylesheet" type="text/css">
11
    <link rel="shortcut icon" type="image/png" href="">
12
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.95.3/css/materialize.min.css">
16
    {{ template "styles" . }}
13
    {{ template "styles" . }}
17
18
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
19
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
20
    <!--[if lt IE 9]>
21
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
22
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
23
    <![endif]-->
24
</head>
14
</head>
25
<body>
15
<body>
26
    {{ template "content" . }}
16
    {{ template "content" . }}
17
18
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
19
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.95.3/js/materialize.min.js"></script>
27
    {{ template "scripts" . }}
20
    {{ template "scripts" . }}
28
</body>
21
</body>
29
</html>
22
</html>

+ 2 - 2
html/workspace.html

1
{{ define "title" }}Workspace{{ end }}
2
{{ define "style" }}{{ end }}
1
{{ define "title" }}Loop Workspace{{ end }}
2
{{ define "styles" }}{{ end }}
3
{{ define "content" }}
3
{{ define "content" }}
4
    <div class="row">
4
    <div class="row">
5
        <div class="col-sm-12">
5
        <div class="col-sm-12">


+ 8 - 8
main.go

5
	"net/http"
5
	"net/http"
6
	"time"
6
	"time"
7
7
8
	"code.revolvingcow.com/revolvingcow/loop/environment"
8
	"code.revolvingcow.com/revolvingcow/loop/handlers"
9
	"code.revolvingcow.com/revolvingcow/loop/handlers"
9
	"github.com/gorilla/mux"
10
	"github.com/gorilla/mux"
10
)
11
)
11
12
12
const (
13
	UserFile = "loop_users"
14
)
15
16
func main() {
13
func main() {
17
	log.Print("Configuring environment")
14
	log.Print("Configuring environment")
18
	time.Local = time.UTC
15
	time.Local = time.UTC
19
	ConfigureEnvironment()
16
	environment.ConfigureEnvironment()
20
17
21
	r := mux.NewRouter().StrictSlash(false)
18
	r := mux.NewRouter().StrictSlash(false)
22
	r.HandleFunc("/", handlers.DefaultHandler)
19
	r.HandleFunc("/", handlers.DefaultHandler)
23
20
24
	content := r.PathPrefix("/{username}/content").Subrouter()
21
	auth := r.PathPrefix("/logon").Subrouter()
22
	auth.Methods("POST").HandlerFunc(handlers.LogonHandler)
23
24
	content := r.PathPrefix("/{username}").Subrouter()
25
	content.Methods("GET").HandlerFunc(handlers.ContentHandler)
25
	content.Methods("GET").HandlerFunc(handlers.ContentHandler)
26
26
27
	article := r.PathPrefix("/{username}/blog/{article}").Subrouter()
27
	article := r.PathPrefix("/{username}/blog/{article}").Subrouter()
30
	blog := r.PathPrefix("/{username}/blog").Subrouter()
30
	blog := r.PathPrefix("/{username}/blog").Subrouter()
31
	blog.Methods("GET").HandlerFunc(handlers.BlogHandler)
31
	blog.Methods("GET").HandlerFunc(handlers.BlogHandler)
32
32
33
	log.Print("Serving content on ", address())
34
	log.Fatal(http.ListenAndServe(address(), nil))
33
	log.Print("Serving content on ", environment.Address())
34
	log.Fatal(http.ListenAndServe(environment.Address(), r))
35
}
35
}

+ 1 - 1
markdown.go

1
package main
1
package markdown
2
2
3
import (
3
import (
4
	"github.com/microcosm-cc/bluemonday"
4
	"github.com/microcosm-cc/bluemonday"

+ 1 - 1
markdown_test.go

1
package main
1
package markdown
2
2
3
import "testing"
3
import "testing"
4
4

+ 6 - 4
security.go

1
package main
1
package security
2
2
3
import (
3
import (
4
	"crypto/aes"
4
	"crypto/aes"
11
	"fmt"
11
	"fmt"
12
	"io"
12
	"io"
13
	"strings"
13
	"strings"
14
15
	"code.revolvingcow.com/revolvingcow/loop/environment"
14
)
16
)
15
17
16
func encodeBase64(data []byte) string {
18
func encodeBase64(data []byte) string {
76
	return data, nil
78
	return data, nil
77
}
79
}
78
80
79
func hash(clearText, salt string) []byte {
81
func Hash(clearText, salt string) []byte {
80
	h := md5.New()
82
	h := md5.New()
81
	h.Write([]byte(clearText))
83
	h.Write([]byte(clearText))
82
	return h.Sum(nil)
84
	return h.Sum(nil)
94
	return sha.Sum(nil)
96
	return sha.Sum(nil)
95
}
97
}
96
98
97
func generatePassphrase(username, passphrase string) string {
98
	return encodeBase64(hashCredentials(username, passphrase, salt()))
99
func GeneratePassphrase(username, passphrase string) string {
100
	return encodeBase64(hashCredentials(username, passphrase, environment.Salt()))
99
}
101
}

+ 4 - 2
security_test.go

1
package main
1
package security
2
2
3
import "testing"
3
import (
4
	"testing"
5
)
4
6
5
func TestEncoding(t *testing.T) {
7
func TestEncoding(t *testing.T) {
6
	clear := "encoding test"
8
	clear := "encoding test"