Skip to content
This repository was archived by the owner on Mar 16, 2021. It is now read-only.

Commit e895d3d

Browse files
author
Jeff Billimek
committed
Merge branch 'master' into admin_view
* master: configurable app DisplayURL for the UI (#121) use redis as a session store backend (#119)
2 parents 82e21aa + 5c005ae commit e895d3d

File tree

11 files changed

+80
-26
lines changed

11 files changed

+80
-26
lines changed

config/example.yaml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
ListenAddr: ':8080' # Consists of 'IP:Port', e.g. ':8080' listens on any IP and on Port 8080
2-
BaseURL: 'http://localhost:3000' # Origin URL, required for the authentication via OAuth
2+
BaseURL: 'http://localhost:3000' # Origin URL, required for the authentication via OAuth callback
3+
DisplayURL: '' # (OPTIONAL) Display URL, how the apication will present itself in the UI - if not set, defaults to BaseURL
34
Backend: boltdb # Can be 'boltdb' or 'redis'
45
DataDir: ./data # Contains: the database and the private key
56
EnableDebugMode: true # Activates more detailed logging
@@ -22,9 +23,11 @@ Proxy: # only relevant when using the proxy authbackend
2223
UserHeader: "X-Goog-Authenticated-User-ID" # pull the unique user ID from this header
2324
DisplayNameHeader: "X-Goog-Authenticated-User-Email" # pull the display naem from this header
2425
Redis:
25-
Host: localhost:6379 # host:port combination; required
26-
Password: replace me # redis connection password; optional; default is none
27-
Db: 0 # redis index (https://redis.io/commands/select); optional; default is 0
28-
MaxRetries: 3 # maximum number of retries for a failed redis command
29-
ReadTimeout: 3s # timeout for read operations; default is 3s. This is a golang time.ParseDuration string
30-
WriteTimeout: 3s # timeout for write operations; default is 3s. This is a golang time.ParseDuration string
26+
Host: localhost:6379 # host:port combination; required
27+
Password: replace me # redis connection password; optional; default is none
28+
Db: 0 # redis index (https://redis.io/commands/select); optional; default is 0
29+
MaxRetries: 3 # maximum number of retries for a failed redis command
30+
ReadTimeout: 3s # timeout for read operations; default is 3s. This is a golang time.ParseDuration string
31+
WriteTimeout: 3s # timeout for write operations; default is 3s. This is a golang time.ParseDuration string
32+
SessionDB: 1 # redis session store index (https://redis.io/commands/select); optional; default is 1
33+
SharedKey: replace me # redis session store shared key; optional; default is "secret"

deployments/cloudfoundry/run.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ fi
1313
if [ "$REDIS" != "" ]; then
1414
export GUS_REDIS_HOST="$(echo $VCAP_SERVICES | jq -r '.["'$REDIS_SERVICE_NAME'"][0].credentials.host'):$(echo $VCAP_SERVICES | jq -r '.["'$REDIS_SERVICE_NAME'"][0].credentials.port')"
1515
export GUS_REDIS_PASSWORD="$(echo $VCAP_SERVICES | jq -r '.["'$REDIS_SERVICE_NAME'"][0].credentials.password')"
16+
export GUS_REDIS_SHARED_KEY=$GUS_REDIS_PASSWORD
1617
fi
1718

1819
echo "#### Starting golang-url-shortener..."

internal/handlers/auth.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,22 @@ import (
99
"github.com/sirupsen/logrus"
1010

1111
jwt "github.com/dgrijalva/jwt-go"
12-
"github.com/gin-gonic/contrib/sessions"
12+
"github.com/gin-contrib/sessions"
13+
"github.com/gin-contrib/sessions/cookie"
14+
"github.com/gin-contrib/sessions/redis"
1315
"github.com/gin-gonic/gin"
1416
"github.com/pkg/errors"
1517
)
1618

1719
func (h *Handler) initOAuth() {
18-
h.engine.Use(sessions.Sessions("backend", sessions.NewCookieStore(util.GetPrivateKey())))
19-
20+
switch backend := util.GetConfig().Backend; backend {
21+
// use redis as the session store if it is configured
22+
case "redis":
23+
store, _ := redis.NewStoreWithDB(10, "tcp", util.GetConfig().Redis.Host, util.GetConfig().Redis.Password, util.GetConfig().Redis.SessionDB, util.GetPrivateKey())
24+
h.engine.Use(sessions.Sessions("backend", store))
25+
default:
26+
h.engine.Use(sessions.Sessions("backend", cookie.NewStore(util.GetPrivateKey())))
27+
}
2028
h.providers = []string{}
2129
google := util.GetConfig().Google
2230
if google.Enabled() {
@@ -39,7 +47,14 @@ func (h *Handler) initOAuth() {
3947

4048
// initProxyAuth intializes data structures for proxy authentication mode
4149
func (h *Handler) initProxyAuth() {
42-
h.engine.Use(sessions.Sessions("backend", sessions.NewCookieStore(util.GetPrivateKey())))
50+
switch backend := util.GetConfig().Backend; backend {
51+
// use redis as the session store if it is configured
52+
case "redis":
53+
store, _ := redis.NewStoreWithDB(10, "tcp", util.GetConfig().Redis.Host, util.GetConfig().Redis.Password, util.GetConfig().Redis.SessionDB, util.GetPrivateKey())
54+
h.engine.Use(sessions.Sessions("backend", store))
55+
default:
56+
h.engine.Use(sessions.Sessions("backend", cookie.NewStore(util.GetPrivateKey())))
57+
}
4358
h.providers = []string{}
4459
h.providers = append(h.providers, "proxy")
4560
h.engine.POST("/api/v1/auth/check", h.handleAuthCheck)

internal/handlers/auth/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"time"
99

1010
jwt "github.com/dgrijalva/jwt-go"
11-
"github.com/gin-gonic/contrib/sessions"
11+
"github.com/gin-contrib/sessions"
1212
"github.com/gin-gonic/gin"
1313
"github.com/mxschmitt/golang-url-shortener/internal/util"
1414
"github.com/pkg/errors"

internal/handlers/handlers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ func (h *Handler) setHandlers() error {
164164
h.engine.GET("/api/v1/info", h.handleInfo)
165165
h.engine.GET("/d/:id/:hash", h.handleDelete)
166166
h.engine.GET("/ok", h.handleHealthcheck)
167+
h.engine.GET("/displayurl", h.handleDisplayURL)
167168

168169
// Handling the shorted URLs, if no one exists, it checks
169170
// in the filesystem and sets headers for caching

internal/handlers/public.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ func (h *Handler) handleInfo(c *gin.Context) {
162162
c.JSON(http.StatusOK, out)
163163
}
164164

165+
// handleDisplayURL returns the URL to use for display purposes
166+
func (h *Handler) handleDisplayURL(c *gin.Context) {
167+
out := util.GetConfig().DisplayURL
168+
c.JSON(http.StatusOK, out)
169+
}
170+
165171
func (h *Handler) handleRecent(c *gin.Context) {
166172
user := c.MustGet("user").(*auth.JWTClaims)
167173
entries, err := h.store.GetUserEntries(user.OAuthProvider, user.OAuthID)

internal/util/config.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
type Configuration struct {
1717
ListenAddr string `yaml:"ListenAddr" env:"LISTEN_ADDR"`
1818
BaseURL string `yaml:"BaseURL" env:"BASE_URL"`
19+
DisplayURL string `yaml:"DisplayURL" env:"DISPLAY_URL"`
1920
DataDir string `yaml:"DataDir" env:"DATA_DIR"`
2021
Backend string `yaml:"Backend" env:"BACKEND"`
2122
AuthBackend string `yaml:"AuthBackend" env:"AUTH_BACKEND"`
@@ -38,6 +39,8 @@ type redisConf struct {
3839
MaxRetries int `yaml:"MaxRetries" env:"MAX_RETRIES"`
3940
ReadTimeout string `yaml:"ReadTimeout" env:"READ_TIMEOUT"`
4041
WriteTimeout string `yaml:"WriteTimeout" env:"WRITE_TIMEOUT"`
42+
SessionDB string `yaml:"SessionDB" env:"SESSION_DB"`
43+
SharedKey string `yaml:"SharedKey" env:"SHARED_KEY"`
4144
}
4245

4346
type oAuthConf struct {
@@ -52,10 +55,11 @@ type proxyAuthConf struct {
5255
DisplayNameHeader string `yaml:"DisplayNameHeader" env:"DISPLAY_NAME_HEADER"`
5356
}
5457

55-
// config contains the default values
58+
// Config contains the default values
5659
var Config = Configuration{
5760
ListenAddr: ":8080",
5861
BaseURL: "http://localhost:3000",
62+
DisplayURL: "",
5963
DataDir: "data",
6064
Backend: "boltdb",
6165
EnableDebugMode: false,
@@ -69,6 +73,8 @@ var Config = Configuration{
6973
MaxRetries: 3,
7074
ReadTimeout: "3s",
7175
WriteTimeout: "3s",
76+
SessionDB: "1",
77+
SharedKey: "secret",
7278
},
7379
}
7480

@@ -106,6 +112,11 @@ func (o oAuthConf) Enabled() bool {
106112

107113
// GetConfig returns the configuration from the memory
108114
func GetConfig() Configuration {
115+
// if DisplayURL is not set in the config, default to BaseURL
116+
if Config.DisplayURL == "" {
117+
Config.DisplayURL = Config.BaseURL
118+
}
119+
109120
return Config
110121
}
111122

internal/util/private.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,10 @@ func CheckForPrivateKey() error {
3535

3636
// GetPrivateKey returns the private key
3737
func GetPrivateKey() []byte {
38-
return privateKey
38+
switch backend := GetConfig().Backend; backend {
39+
case "redis":
40+
return []byte(GetConfig().Redis.SharedKey)
41+
default:
42+
return privateKey
43+
}
3944
}

web/src/Home/Home.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export default class HomeComponent extends Component {
1818
usedSettings: this.urlParams.get('customUrl') ? ['custom'] : [],
1919
customID: this.urlParams.get('customUrl') ? this.urlParams.get('customUrl') : '',
2020
showCustomIDError: false,
21-
expiration: null
21+
expiration: null,
22+
displayURL: window.location.origin
2223
}
2324
}
2425
handleURLChange = (e, { value }) => this.url = value
@@ -31,12 +32,13 @@ export default class HomeComponent extends Component {
3132
onSettingsChange = (e, { value }) => {
3233
this.setState({ usedSettings: value })
3334
}
34-
35-
3635

37-
36+
3837
componentDidMount() {
3938
this.urlInput.focus()
39+
fetch("/displayurl")
40+
.then(response => response.json())
41+
.then(data => this.setState({displayURL: data}));
4042
}
4143
handleURLSubmit = () => {
4244
if (!this.state.showCustomIDError) {
@@ -47,7 +49,7 @@ export default class HomeComponent extends Component {
4749
Password: this.state.usedSettings.includes("protected") && this.password ? this.password : undefined
4850
}, r => this.setState({
4951
links: [...this.state.links, {
50-
shortenedURL: r.URL,
52+
shortenedURL: this.state.displayURL + "/" + this.state.customID,
5153
originalURL: this.url,
5254
expiration: this.state.usedSettings.includes("expire") && this.state.expiration ? this.state.expiration.toISOString() : undefined,
5355
deletionURL: r.DeletionURL
@@ -89,7 +91,7 @@ export default class HomeComponent extends Component {
8991
</MediaQuery>
9092
<Form.Group style={{ marginBottom: "1rem" }}>
9193
{usedSettings.includes("custom") && <Form.Field error={showCustomIDError} width={16}>
92-
<Input label={window.location.origin + "/"} onChange={this.handleCustomIDChange} placeholder='my-shortened-url' value={this.state.customID}/>
94+
<Input label={this.state.displayURL + "/"} onChange={this.handleCustomIDChange} placeholder='my-shortened-url' value={this.state.customID}/>
9395
</Form.Field>}
9496
</Form.Group>
9597
<Form.Group widths="equal">

web/src/Lookup/Lookup.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,21 @@ import CustomCard from '../Card/Card'
66

77
export default class LookupComponent extends Component {
88
state = {
9-
links: []
9+
links: [],
10+
displayURL: window.location.origin
11+
}
12+
componentDidMount() {
13+
fetch("/displayurl")
14+
.then(response => response.json())
15+
.then(data => this.setState({displayURL: data}));
1016
}
1117
handleURLChange = (e, { value }) => this.url = value
1218
handleURLSubmit = () => {
13-
let id = this.url.replace(window.location.origin + "/", "")
19+
let id = this.url.replace(this.state.displayURL + "/", "")
1420
util.lookupEntry(id, res => this.setState({
1521
links: [...this.state.links, [
1622
res.URL,
17-
this.url,
23+
this.state.displayURL + "/" + this.url,
1824
this.VisitCount,
1925
res.CratedOn,
2026
res.LastVisit,
@@ -30,7 +36,7 @@ export default class LookupComponent extends Component {
3036
<Header size='huge'>URL Lookup</Header>
3137
<Form onSubmit={this.handleURLSubmit}>
3238
<Form.Field>
33-
<Input required size='big' ref={input => this.urlInput = input} action={{ icon: 'arrow right', labelPosition: 'right', content: 'Lookup' }} type='url' onChange={this.handleURLChange} name='url' placeholder={window.location.origin + "/..."} autoComplete="off" />
39+
<Input required size='big' ref={input => this.urlInput = input} action={{ icon: 'arrow right', labelPosition: 'right', content: 'Lookup' }} type='text' label={this.state.displayURL + "/"} onChange={this.handleURLChange} name='url' placeholder={"short url"} autoComplete="off" />
3440
</Form.Field>
3541
</Form>
3642
</Segment>

0 commit comments

Comments
 (0)