mirror of
https://github.com/nkanaev/yarr.git
synced 2025-05-24 00:33:14 +00:00
Add non-root url path support
This commit is contained in:
parent
63b265fa04
commit
4f79c919f0
@ -90,12 +90,12 @@
|
||||
Import
|
||||
</label>
|
||||
</b-dropdown-form>
|
||||
<b-dropdown-item href="/opml/export">
|
||||
<b-dropdown-item href="./opml/export">
|
||||
<span class="icon mr-1">{% inline "upload.svg" %}</span>
|
||||
Export
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-divider v-if="authenticated"></b-dropdown-divider>
|
||||
<b-dropdown-item-button v-if="authenticated">
|
||||
<b-dropdown-item-button v-if="authenticated" @click="logout()">
|
||||
<span class="icon mr-1">{% inline "log-out.svg" %}</span>
|
||||
Log out
|
||||
</b-dropdown-item-button>
|
||||
@ -137,7 +137,7 @@
|
||||
<input type="radio" name="feed" :value="'feed:'+feed.id" v-model="feedSelected">
|
||||
<div class="selectgroup-label d-flex align-items-center w-100">
|
||||
<span class="icon mr-2" v-if="!feed.has_icon">{% inline "rss.svg" %}</span>
|
||||
<span class="icon mr-2" v-else><img v-lazy="'/api/feeds/'+feed.id+'/icon'" alt=""></span>
|
||||
<span class="icon mr-2" v-else><img v-lazy="'./api/feeds/'+feed.id+'/icon'" alt=""></span>
|
||||
<span class="flex-fill text-left text-truncate">{{ feed.title }}</span>
|
||||
<span class="counter text-right">{{ filteredFeedStats[feed.id] || '' }}</span>
|
||||
</div>
|
||||
@ -334,7 +334,7 @@
|
||||
<div v-for="feed in folder.feeds" class="list-row d-flex align-items-center" :key="feed.id">
|
||||
<div class="w-100 text-truncate">
|
||||
<span class="icon mr-2" v-if="!feed.has_icon">{% inline "rss.svg" %}</span>
|
||||
<span class="icon mr-2" v-else><img v-lazy="'/api/feeds/'+feed.id+'/icon'" alt=""></span>
|
||||
<span class="icon mr-2" v-else><img v-lazy="'./api/feeds/'+feed.id+'/icon'" alt=""></span>
|
||||
{{ feed.title }}
|
||||
</div>
|
||||
<span class="icon flex-shrink-0 mx-2"
|
||||
|
@ -26,74 +26,74 @@
|
||||
window.api = {
|
||||
feeds: {
|
||||
list: function() {
|
||||
return api('get', '/api/feeds').then(json)
|
||||
return api('get', './api/feeds').then(json)
|
||||
},
|
||||
create: function(data) {
|
||||
return api('post', '/api/feeds', data).then(json)
|
||||
return api('post', './api/feeds', data).then(json)
|
||||
},
|
||||
update: function(id, data) {
|
||||
return api('put', '/api/feeds/' + id, data)
|
||||
return api('put', './api/feeds/' + id, data)
|
||||
},
|
||||
delete: function(id) {
|
||||
return api('delete', '/api/feeds/' + id)
|
||||
return api('delete', './api/feeds/' + id)
|
||||
},
|
||||
list_items: function(id) {
|
||||
return api('get', '/api/feeds/' + id + '/items').then(json)
|
||||
return api('get', './api/feeds/' + id + '/items').then(json)
|
||||
},
|
||||
refresh: function() {
|
||||
return api('post', '/api/feeds/refresh')
|
||||
return api('post', './api/feeds/refresh')
|
||||
},
|
||||
list_errors: function() {
|
||||
return api('get', '/api/feeds/errors').then(json)
|
||||
return api('get', './api/feeds/errors').then(json)
|
||||
},
|
||||
},
|
||||
folders: {
|
||||
list: function() {
|
||||
return api('get', '/api/folders').then(json)
|
||||
return api('get', './api/folders').then(json)
|
||||
},
|
||||
create: function(data) {
|
||||
return api('post', '/api/folders', data).then(json)
|
||||
return api('post', './api/folders', data).then(json)
|
||||
},
|
||||
update: function(id, data) {
|
||||
return api('put', '/api/folders/' + id, data)
|
||||
return api('put', './api/folders/' + id, data)
|
||||
},
|
||||
delete: function(id) {
|
||||
return api('delete', '/api/folders/' + id)
|
||||
return api('delete', './api/folders/' + id)
|
||||
},
|
||||
list_items: function(id) {
|
||||
return api('get', '/api/folders/' + id + '/items').then(json)
|
||||
return api('get', './api/folders/' + id + '/items').then(json)
|
||||
}
|
||||
},
|
||||
items: {
|
||||
list: function(query) {
|
||||
return api('get', '/api/items' + param(query)).then(json)
|
||||
return api('get', './api/items' + param(query)).then(json)
|
||||
},
|
||||
update: function(id, data) {
|
||||
return api('put', '/api/items/' + id, data)
|
||||
return api('put', './api/items/' + id, data)
|
||||
},
|
||||
mark_read: function(query) {
|
||||
return api('put', '/api/items' + param(query))
|
||||
return api('put', './api/items' + param(query))
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
get: function() {
|
||||
return api('get', '/api/settings').then(json)
|
||||
return api('get', './api/settings').then(json)
|
||||
},
|
||||
update: function(data) {
|
||||
return api('put', '/api/settings', data)
|
||||
return api('put', './api/settings', data)
|
||||
},
|
||||
},
|
||||
status: function() {
|
||||
return api('get', '/api/status').then(json)
|
||||
return api('get', './api/status').then(json)
|
||||
},
|
||||
upload_opml: function(form) {
|
||||
return fetch('/opml/import', {
|
||||
return fetch('./opml/import', {
|
||||
method: 'post',
|
||||
body: new FormData(form),
|
||||
})
|
||||
},
|
||||
crawl: function(url) {
|
||||
return fetch('/page?url=' + url).then(function(res) {
|
||||
return fetch('./page?url=' + url).then(function(res) {
|
||||
return res.text()
|
||||
})
|
||||
}
|
||||
|
20
main.go
20
main.go
@ -4,14 +4,15 @@ import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/nkanaev/yarr/platform"
|
||||
"github.com/nkanaev/yarr/server"
|
||||
"github.com/nkanaev/yarr/storage"
|
||||
sdopen "github.com/skratchdot/open-golang/open"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/nkanaev/yarr/platform"
|
||||
"github.com/nkanaev/yarr/server"
|
||||
"github.com/nkanaev/yarr/storage"
|
||||
sdopen "github.com/skratchdot/open-golang/open"
|
||||
)
|
||||
|
||||
var Version string = "0.0"
|
||||
@ -22,6 +23,7 @@ func main() {
|
||||
var ver, open bool
|
||||
flag.StringVar(&addr, "addr", "127.0.0.1:7070", "address to run server on")
|
||||
flag.StringVar(&authfile, "auth-file", "", "path to a file containing username:password")
|
||||
flag.StringVar(&server.BasePath, "base", "", "base path of the service url")
|
||||
flag.StringVar(&certfile, "cert-file", "", "path to cert file for https")
|
||||
flag.StringVar(&keyfile, "key-file", "", "path to key file for https")
|
||||
flag.StringVar(&db, "db", "", "storage file path")
|
||||
@ -34,6 +36,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if server.BasePath != "" && !strings.HasPrefix(server.BasePath, "/") {
|
||||
server.BasePath = "/" + server.BasePath
|
||||
}
|
||||
|
||||
if server.BasePath != "" && strings.HasSuffix(server.BasePath, "/") {
|
||||
server.BasePath = strings.TrimSuffix(server.BasePath, "/")
|
||||
}
|
||||
|
||||
server.BasePathReady <- true
|
||||
|
||||
logger := log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
configPath, err := os.UserConfigDir()
|
||||
|
@ -24,10 +24,17 @@ func userIsAuthenticated(req *http.Request, username, password string) bool {
|
||||
|
||||
func userAuthenticate(rw http.ResponseWriter, username, password string) {
|
||||
expires := time.Now().Add(time.Hour * 24 * 7) // 1 week
|
||||
var cookiePath string
|
||||
if BasePath != "" {
|
||||
cookiePath = BasePath
|
||||
} else {
|
||||
cookiePath = "/"
|
||||
}
|
||||
cookie := http.Cookie{
|
||||
Name: "auth",
|
||||
Value: username + ":" + secret(username, password),
|
||||
Expires: expires,
|
||||
Path: cookiePath,
|
||||
}
|
||||
http.SetCookie(rw, &cookie)
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/nkanaev/yarr/storage"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
@ -19,27 +18,41 @@ import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nkanaev/yarr/storage"
|
||||
)
|
||||
|
||||
var routes []Route = []Route{
|
||||
p("/", IndexHandler).ManualAuth(),
|
||||
p("/static/*path", StaticHandler).ManualAuth(),
|
||||
var routes []Route
|
||||
|
||||
p("/api/status", StatusHandler),
|
||||
p("/api/folders", FolderListHandler),
|
||||
p("/api/folders/:id", FolderHandler),
|
||||
p("/api/feeds", FeedListHandler),
|
||||
p("/api/feeds/find", FeedHandler),
|
||||
p("/api/feeds/refresh", FeedRefreshHandler),
|
||||
p("/api/feeds/errors", FeedErrorsHandler),
|
||||
p("/api/feeds/:id/icon", FeedIconHandler),
|
||||
p("/api/feeds/:id", FeedHandler),
|
||||
p("/api/items", ItemListHandler),
|
||||
p("/api/items/:id", ItemHandler),
|
||||
p("/api/settings", SettingsHandler),
|
||||
p("/opml/import", OPMLImportHandler),
|
||||
p("/opml/export", OPMLExportHandler),
|
||||
p("/page", PageCrawlHandler),
|
||||
var BasePathReady = make(chan bool)
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
<-BasePathReady
|
||||
routes = []Route{
|
||||
p(BasePath+"/", IndexHandler).ManualAuth(),
|
||||
p(BasePath+"/static/*path", StaticHandler).ManualAuth(),
|
||||
|
||||
p(BasePath+"/api/status", StatusHandler),
|
||||
p(BasePath+"/api/folders", FolderListHandler),
|
||||
p(BasePath+"/api/folders/:id", FolderHandler),
|
||||
p(BasePath+"/api/feeds", FeedListHandler),
|
||||
p(BasePath+"/api/feeds/find", FeedHandler),
|
||||
p(BasePath+"/api/feeds/refresh", FeedRefreshHandler),
|
||||
p(BasePath+"/api/feeds/errors", FeedErrorsHandler),
|
||||
p(BasePath+"/api/feeds/:id/icon", FeedIconHandler),
|
||||
p(BasePath+"/api/feeds/:id", FeedHandler),
|
||||
p(BasePath+"/api/items", ItemListHandler),
|
||||
p(BasePath+"/api/items/:id", ItemHandler),
|
||||
p(BasePath+"/api/settings", SettingsHandler),
|
||||
p(BasePath+"/opml/import", OPMLImportHandler),
|
||||
p(BasePath+"/opml/export", OPMLExportHandler),
|
||||
p(BasePath+"/page", PageCrawlHandler),
|
||||
}
|
||||
if BasePath != "" {
|
||||
routes = append(routes, p(BasePath, RedirectBasePathHandler).ManualAuth())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
@ -140,6 +153,11 @@ func IndexHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
func StaticHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
path := Vars(req)["path"]
|
||||
|
||||
if BasePath != "" {
|
||||
path = strings.TrimPrefix(path, BasePath)
|
||||
}
|
||||
|
||||
ctype := mime.TypeByExtension(filepath.Ext(path))
|
||||
|
||||
if assets != nil {
|
||||
@ -528,3 +546,7 @@ func PageCrawlHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RedirectBasePathHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(rw, req, req.URL.Path+"/", http.StatusFound)
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var BasePath string = ""
|
||||
|
||||
type Route struct {
|
||||
url string
|
||||
urlRegex *regexp.Regexp
|
||||
|
Loading…
x
Reference in New Issue
Block a user