Commit a76c71cc authored by zauberstuhl's avatar zauberstuhl
Browse files

Vuejs

parent 9f4a3d89
config.yml config.yml
*.db *.db
vendor/
unmappd unmappd
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
FROM golang:buster FROM golang:buster
RUN apt-get update && apt-get install -y npm
RUN npm install --global yarn
ADD . /go/src/build ADD . /go/src/build
WORKDIR /go/src/build WORKDIR /go/src/build
RUN yarn build
RUN go build -o unmappd RUN go build -o unmappd
FROM debian:buster FROM debian:buster
...@@ -12,13 +16,13 @@ RUN apt-get clean && apt-get autoclean ...@@ -12,13 +16,13 @@ RUN apt-get clean && apt-get autoclean
RUN adduser --disabled-password app RUN adduser --disabled-password app
ADD . /home/app COPY --from=0 /go/src/build/dist/ /home/app/dist/
COPY --from=0 /go/src/build/unmappd /home/app/unmappd COPY --from=0 /go/src/build/unmappd /home/app/
COPY --from=0 /go/src/build/config.yml.example /home/app/config.yml
RUN chmod +x /home/app/unmappd RUN chmod +x /home/app/unmappd
WORKDIR /home/app WORKDIR /home/app
RUN cp config.yml.example config.yml
RUN rm -rf vendor *.db
EXPOSE 8080 EXPOSE 8080
......
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
## Build ## Build
### Compile assets
yarn install
yarn build
### Compile binary
go build -o unmappd go build -o unmappd
## Docker image ## Docker image
......
...@@ -94,7 +94,7 @@ func auth_handler(w http.ResponseWriter, r *http.Request) { ...@@ -94,7 +94,7 @@ func auth_handler(w http.ResponseWriter, r *http.Request) {
return return
} }
userParam := "/users/%s" userParam := "#/map/%s"
err = db.First(&User{}, "username = ?", username).Error err = db.First(&User{}, "username = ?", username).Error
if err == nil { if err == nil {
http.Redirect(w, r, http.Redirect(w, r,
...@@ -135,7 +135,11 @@ func fetch_auth(code string) (string, error) { ...@@ -135,7 +135,11 @@ func fetch_auth(code string) (string, error) {
var uauth Uauthresp var uauth Uauthresp
return uauth.Response.Access_token, get(fmt.Sprintf( return uauth.Response.Access_token, get(fmt.Sprintf(
"https://untappd.com/oauth/authorize/?client_id=%s&client_secret=%s&response_type=code&redirect_url=%s&code=%s", "https://untappd.com/oauth/authorize/?client_id=%s&client_secret=%s&response_type=code&redirect_url=%s&code=%s",
CLIENTID, CLIENTSECRET, REDIRECTURL, code), &uauth) viper.GetString("clientID"),
viper.GetString("clientSecret"),
viper.GetString("redirectUrl"),
code,
), &uauth)
} }
func fetch_user_info(token string) (info Uinfo, err error) { func fetch_user_info(token string) (info Uinfo, err error) {
......
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
redirectUrl: http://localhost:8080 redirectUrl: http://localhost:8080/api/v0/auth
# untappd app secret # untappd app secret
clientID: 1234 clientID: 1234
......
...@@ -25,8 +25,6 @@ import ( ...@@ -25,8 +25,6 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var CLIENTID, CLIENTSECRET, REDIRECTURL string
func init() { func init() {
viper.SetConfigName("config") viper.SetConfigName("config")
viper.AddConfigPath(".") viper.AddConfigPath(".")
...@@ -44,10 +42,6 @@ func init() { ...@@ -44,10 +42,6 @@ func init() {
log.Print("Run migrations..") log.Print("Run migrations..")
db.AutoMigrate(&Location{}) db.AutoMigrate(&Location{})
db.AutoMigrate(&User{}) db.AutoMigrate(&User{})
CLIENTID = viper.GetString("clientID")
CLIENTSECRET = viper.GetString("clientSecret")
REDIRECTURL = viper.GetString("redirectUrl")
} }
func main() { func main() {
...@@ -55,12 +49,15 @@ func main() { ...@@ -55,12 +49,15 @@ func main() {
Addr: "0.0.0.0:8080", Addr: "0.0.0.0:8080",
} }
http.HandleFunc("/", index_handler) http.HandleFunc("/api/v0/config", config_handler)
http.HandleFunc("/users/", user_handler) http.HandleFunc("/api/v0/users/", user_handler)
http.HandleFunc("/api/v0/locations/", location_handler)
http.HandleFunc("/api/v0/auth", auth_handler)
// NOTE will be obsolete in future
http.HandleFunc("/auth", auth_handler) http.HandleFunc("/auth", auth_handler)
// assets directory // assets directory
fs := http.FileServer(http.Dir("public")) fs := http.FileServer(http.Dir("dist"))
http.Handle("/assets/", http.StripPrefix("/assets/", fs)) http.Handle("/", http.StripPrefix("/", fs))
log.Print("Listening on 0.0.0.0:8080 ..") log.Print("Listening on 0.0.0.0:8080 ..")
server.ListenAndServe() server.ListenAndServe()
......
...@@ -20,12 +20,9 @@ package main ...@@ -20,12 +20,9 @@ package main
import ( import (
"time" "time"
"html/template"
"strings"
"fmt" "fmt"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/microcosm-cc/bluemonday"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
_ "github.com/jinzhu/gorm/dialects/mysql" _ "github.com/jinzhu/gorm/dialects/mysql"
...@@ -33,22 +30,22 @@ import ( ...@@ -33,22 +30,22 @@ import (
) )
type User struct { type User struct {
ID uint `gorm:"primary_key"` ID uint `gorm:"primary_key" json:"id"`
CreatedAt time.Time CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time UpdatedAt time.Time `json:"-"`
Username string `gorm:"unique_index;size:191"` Username string `gorm:"unique_index;size:191" json:"-"`
Token string `gorm:"index;size:191"` Token string `gorm:"index;size:191" json:"-"`
TotalBadges int TotalBadges int `json:"totalBadges"`
TotalFriends int TotalFriends int `json:"totalFriends"`
TotalCheckins int TotalCheckins int `json:"totalCheckins"`
TotalBeers int TotalBeers int `json:"totalBeers"`
TotalCreatedBeers int TotalCreatedBeers int `json:"totalCreatedBeers"`
TotalFollowings int TotalFollowings int `json:"totalFollowings"`
TotalPhotos int TotalPhotos int `json:"totalPhotos"`
Locations Locations Locations Locations `json:"-"`
} }
type Users []User type Users []User
...@@ -58,47 +55,22 @@ func (user *User) AfterFind(db *gorm.DB) error { ...@@ -58,47 +55,22 @@ func (user *User) AfterFind(db *gorm.DB) error {
} }
type Location struct { type Location struct {
ID uint `gorm:"primary_key"` ID uint `gorm:"primary_key" json:"id"`
CreatedAt time.Time CreatedAt time.Time `json:"-"`
UserID uint `gorm:"index"` UserID uint `gorm:"index" json:"-"`
Lat, Lng float64 Lat float64 `json:"lat"`
Url, Name, Category, Icon string Lng float64 `json:"lng"`
Url string `json:"url"`
Name string `json:"name"`
Category string `json:"category"`
Icon string `json:"icon"`
} }
type Locations []Location type Locations []Location
func (locations Locations) LatLngJSON() template.JS {
var result string
var format = `[%f,%f,{
timestamp:%d,
venue:{
icon:"%s",
name:"%s",
url:"%s",
category:"%s"
}
}],`
html := bluemonday.UGCPolicy()
for _, location := range locations {
result += fmt.Sprintf(format,
location.Lat,
location.Lng,
location.CreatedAt.Unix(),
location.Icon,
html.Sanitize(location.Name),
location.Url,
html.Sanitize(location.Category),
)
}
// trim last comma
result = strings.TrimRight(result, ",")
result = fmt.Sprintf("[%s]", result)
return template.JS(result)
}
func openDatabase() (*gorm.DB, error) { func openDatabase() (*gorm.DB, error) {
driver := viper.GetString("db.driver") driver := viper.GetString("db.driver")
url := viper.GetString("db.url") url := viper.GetString("db.url")
...@@ -117,3 +89,18 @@ func openDatabase() (*gorm.DB, error) { ...@@ -117,3 +89,18 @@ func openDatabase() (*gorm.DB, error) {
db.LogMode(true) db.LogMode(true)
return db, err return db, err
} }
func userByUsername(username string) (*User, error) {
db, err := openDatabase()
if err != nil {
return nil, err
}
defer db.Close()
var user User
err = db.First(&user, "username = ?", username).Error
if err == nil && viper.GetBool("worker") && len(user.Locations) == 0 {
go checkin_worker(&user, true)
}
return &user, err
}
{
"name": "unmappd",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.21.2",
"core-js": "^3.6.5",
"leaflet": "^1.7.1",
"leaflet-gesture-handling": "^1.2.1",
"vue": "^2.6.12",
"vue-router": "^3.5.1",
"vue2-leaflet": "^2.6.0",
"vue2-leaflet-markercluster": "^3.1.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
html, body {
height: 100%;
}
#mapid {
height: calc(100% - 96px);
height: -o-calc(100% - 96px);
height: -webkit-calc(100% - 96px);
height: -moz-calc(100% - 96px);
}
This diff is collapsed.
.leaflet-control-playback{position:relative;background-color:#7cbdf5;padding:10px;}
.leaflet-control-playback .optionsContainer{position:relative;}
.leaflet-control-playback .optionsContainer > div {
display: inline-block;
}
.leaflet-control-playback .buttonContainer {}
.leaflet-control-playback .buttonContainer a {
display: inline-block;
width: 32px;
height: 32px;
text-decoration: none;
}
.leaflet-control-playback .buttonContainer .btn-stop {
background: url(../images/icon-play.png) no-repeat center;
}
.leaflet-control-playback .buttonContainer .btn-start {
background: url(../images/icon-stop.png) no-repeat center;
}
.leaflet-control-playback .buttonContainer .btn-restart {
background: url(../images/icon-restart.png) no-repeat center;
}
.leaflet-control-playback .buttonContainer .btn-slow {
background: url(../images/icon-slow.png) no-repeat center;
}
.leaflet-control-playback .buttonContainer .btn-quick {
background: url(../images/icon-quick.png) no-repeat center;
}
.leaflet-control-playback .buttonContainer .btn-close {
background: url(../images/icon-close.png) no-repeat center;
}
.leaflet-control-playback .infoContainer {}
.leaflet-control-playback .sliderContainer {}
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg,
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
max-width: none !important;
max-height: none !important;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile {
will-change: opacity;