mirror of
https://git.phreedom.club/localhost_frssoft/bloat.git
synced 2024-11-24 21:01:28 +00:00
Merge patch and add useredit template...
Patch from: https://git.freesoftwareextremist.com/bloat/commit/?id=887ed241d64ba5db3fd3d87194fb5595e5ad7d73 Patch description: Use cookies for session Remove the server side session storage and store all the session related data in the client side cookies. This decreases the exposure of the auth tokens. It also simplifies the installation process as bloat no longer requires write access to the filesystem. This is a breaking change, all the existing sessions will stop working.
This commit is contained in:
parent
42badedeeb
commit
f9702f81a9
10
INSTALL
10
INSTALL
|
@ -23,16 +23,8 @@ most cases, you only need to change the value of "client_website".
|
||||||
# cp bloat.gen.conf /etc/bloat.conf
|
# cp bloat.gen.conf /etc/bloat.conf
|
||||||
# $EDITOR /etc/bloat.conf
|
# $EDITOR /etc/bloat.conf
|
||||||
|
|
||||||
4. Create database directory
|
|
||||||
Create a directory to store session information. Optionally, create a user
|
|
||||||
to run bloat and change the ownership of the database directory accordingly.
|
|
||||||
# mkdir /var/bloat
|
|
||||||
# useradd _bloat
|
|
||||||
# chown -R _bloat:_bloat /var/bloat
|
|
||||||
Replace /var/bloat with the value you specified in the config file.
|
|
||||||
|
|
||||||
5. Run the binary
|
5. Run the binary
|
||||||
# su _bloat -c bloat
|
$ bloat
|
||||||
Now you should create an init script to automatically start bloat at system
|
Now you should create an init script to automatically start bloat at system
|
||||||
startup.
|
startup.
|
||||||
|
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -10,7 +10,6 @@ SRC=main.go \
|
||||||
mastodon/*.go \
|
mastodon/*.go \
|
||||||
model/*.go \
|
model/*.go \
|
||||||
renderer/*.go \
|
renderer/*.go \
|
||||||
repo/*.go \
|
|
||||||
service/*.go \
|
service/*.go \
|
||||||
util/*.go \
|
util/*.go \
|
||||||
|
|
||||||
|
@ -18,8 +17,7 @@ all: bloat
|
||||||
|
|
||||||
bloat: $(SRC) $(TMPL)
|
bloat: $(SRC) $(TMPL)
|
||||||
$(GO) build $(GOFLAGS) -o bloat main.go
|
$(GO) build $(GOFLAGS) -o bloat main.go
|
||||||
sed -e "s%=database%=/var/bloat%g" \
|
sed -e "s%=templates%=$(SHAREPATH)/templates%g" \
|
||||||
-e "s%=templates%=$(SHAREPATH)/templates%g" \
|
|
||||||
-e "s%=static%=$(SHAREPATH)/static%g" \
|
-e "s%=static%=$(SHAREPATH)/static%g" \
|
||||||
< bloat.conf > bloat.gen.conf
|
< bloat.conf > bloat.gen.conf
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
# - Key and Value are separated by a single '='
|
# - Key and Value are separated by a single '='
|
||||||
# - Leading and trailing white spaces in Key and Value are ignored
|
# - Leading and trailing white spaces in Key and Value are ignored
|
||||||
# - Quoting and multi-line values are not supported
|
# - Quoting and multi-line values are not supported
|
||||||
#
|
|
||||||
# Changing values of client_name, client_scope or client_website will cause
|
|
||||||
# previously generated access tokens and client tokens to be invalid. Issue the
|
|
||||||
# `rm -r database_path/*` command to clean the database afterwards.
|
|
||||||
|
|
||||||
# Address to listen to. Value can be of "HOSTNAME:PORT" or "IP:PORT" form. In
|
# Address to listen to. Value can be of "HOSTNAME:PORT" or "IP:PORT" form. In
|
||||||
# case of empty HOSTNAME or IP, "0.0.0.0:PORT" is used.
|
# case of empty HOSTNAME or IP, "0.0.0.0:PORT" is used.
|
||||||
|
@ -19,15 +15,12 @@ listen_address=127.0.0.1:8080
|
||||||
client_website=http://127.0.0.1:8080
|
client_website=http://127.0.0.1:8080
|
||||||
|
|
||||||
# Name of the client.
|
# Name of the client.
|
||||||
client_name=bloat
|
client_name=custom bloat
|
||||||
|
|
||||||
# Mastadon scopes used by the client.
|
# Mastadon scopes used by the client.
|
||||||
# See https://docs.joinmastodon.org/api/oauth-scopes/
|
# See https://docs.joinmastodon.org/api/oauth-scopes/
|
||||||
client_scope=read write follow
|
client_scope=read write follow
|
||||||
|
|
||||||
# Path of database directory. It's used to store session information.
|
|
||||||
database_path=database
|
|
||||||
|
|
||||||
# Path of directory containing template files.
|
# Path of directory containing template files.
|
||||||
templates_path=templates
|
templates_path=templates
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ type config struct {
|
||||||
SingleInstance string
|
SingleInstance string
|
||||||
StaticDirectory string
|
StaticDirectory string
|
||||||
TemplatesPath string
|
TemplatesPath string
|
||||||
DatabasePath string
|
|
||||||
CustomCSS string
|
CustomCSS string
|
||||||
PostFormats []model.PostFormat
|
PostFormats []model.PostFormat
|
||||||
LogFile string
|
LogFile string
|
||||||
|
@ -30,8 +29,7 @@ func (c *config) IsValid() bool {
|
||||||
len(c.ClientScope) < 1 ||
|
len(c.ClientScope) < 1 ||
|
||||||
len(c.ClientWebsite) < 1 ||
|
len(c.ClientWebsite) < 1 ||
|
||||||
len(c.StaticDirectory) < 1 ||
|
len(c.StaticDirectory) < 1 ||
|
||||||
len(c.TemplatesPath) < 1 ||
|
len(c.TemplatesPath) < 1 {
|
||||||
len(c.DatabasePath) < 1 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -75,10 +73,10 @@ func Parse(r io.Reader) (c *config, err error) {
|
||||||
c.StaticDirectory = val
|
c.StaticDirectory = val
|
||||||
case "templates_path":
|
case "templates_path":
|
||||||
c.TemplatesPath = val
|
c.TemplatesPath = val
|
||||||
case "database_path":
|
|
||||||
c.DatabasePath = val
|
|
||||||
case "custom_css":
|
case "custom_css":
|
||||||
c.CustomCSS = val
|
c.CustomCSS = val
|
||||||
|
case "database_path":
|
||||||
|
// ignore
|
||||||
case "post_formats":
|
case "post_formats":
|
||||||
vals := strings.Split(val, ",")
|
vals := strings.Split(val, ",")
|
||||||
var formats []model.PostFormat
|
var formats []model.PostFormat
|
||||||
|
|
24
main.go
24
main.go
|
@ -12,9 +12,7 @@ import (
|
||||||
|
|
||||||
"bloat/config"
|
"bloat/config"
|
||||||
"bloat/renderer"
|
"bloat/renderer"
|
||||||
"bloat/repo"
|
|
||||||
"bloat/service"
|
"bloat/service"
|
||||||
"bloat/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -48,26 +46,6 @@ func main() {
|
||||||
errExit(err)
|
errExit(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Mkdir(config.DatabasePath, 0755)
|
|
||||||
if err != nil && !os.IsExist(err) {
|
|
||||||
errExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionDBPath := filepath.Join(config.DatabasePath, "session")
|
|
||||||
sessionDB, err := util.NewDatabse(sessionDBPath)
|
|
||||||
if err != nil {
|
|
||||||
errExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
appDBPath := filepath.Join(config.DatabasePath, "app")
|
|
||||||
appDB, err := util.NewDatabse(appDBPath)
|
|
||||||
if err != nil {
|
|
||||||
errExit(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionRepo := repo.NewSessionRepo(sessionDB)
|
|
||||||
appRepo := repo.NewAppRepo(appDB)
|
|
||||||
|
|
||||||
customCSS := config.CustomCSS
|
customCSS := config.CustomCSS
|
||||||
if len(customCSS) > 0 && !strings.HasPrefix(customCSS, "http://") &&
|
if len(customCSS) > 0 && !strings.HasPrefix(customCSS, "http://") &&
|
||||||
!strings.HasPrefix(customCSS, "https://") {
|
!strings.HasPrefix(customCSS, "https://") {
|
||||||
|
@ -89,7 +67,7 @@ func main() {
|
||||||
|
|
||||||
s := service.NewService(config.ClientName, config.ClientScope,
|
s := service.NewService(config.ClientName, config.ClientScope,
|
||||||
config.ClientWebsite, customCSS, config.SingleInstance,
|
config.ClientWebsite, customCSS, config.SingleInstance,
|
||||||
config.PostFormats, renderer, sessionRepo, appRepo)
|
config.PostFormats, renderer)
|
||||||
handler := service.NewHandler(s, logger, config.StaticDirectory)
|
handler := service.NewHandler(s, logger, config.StaticDirectory)
|
||||||
|
|
||||||
logger.Println("listening on", config.ListenAddress)
|
logger.Println("listening on", config.ListenAddress)
|
||||||
|
|
21
model/app.go
21
model/app.go
|
@ -1,21 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrAppNotFound = errors.New("app not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
type App struct {
|
|
||||||
InstanceDomain string `json:"instance_domain"`
|
|
||||||
InstanceURL string `json:"instance_url"`
|
|
||||||
ClientID string `json:"client_id"`
|
|
||||||
ClientSecret string `json:"client_secret"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppRepo interface {
|
|
||||||
Add(app App) (err error)
|
|
||||||
Get(instanceDomain string) (app App, err error)
|
|
||||||
}
|
|
|
@ -1,28 +1,52 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrSessionNotFound = errors.New("session not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id,omitempty"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"uid,omitempty"`
|
||||||
InstanceDomain string `json:"instance_domain"`
|
Instance string `json:"ins,omitempty"`
|
||||||
AccessToken string `json:"access_token"`
|
ClientID string `json:"cid,omitempty"`
|
||||||
CSRFToken string `json:"csrf_token"`
|
ClientSecret string `json:"cs,omitempty"`
|
||||||
Settings Settings `json:"settings"`
|
AccessToken string `json:"at,omitempty"`
|
||||||
}
|
CSRFToken string `json:"csrf,omitempty"`
|
||||||
|
Settings Settings `json:"sett,omitempty"`
|
||||||
type SessionRepo interface {
|
|
||||||
Add(session Session) (err error)
|
|
||||||
Get(sessionID string) (session Session, err error)
|
|
||||||
Remove(sessionID string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Session) IsLoggedIn() bool {
|
func (s Session) IsLoggedIn() bool {
|
||||||
return len(s.AccessToken) > 0
|
return len(s.AccessToken) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
DefaultVisibility string `json:"dv,omitempty"`
|
||||||
|
DefaultFormat string `json:"df,omitempty"`
|
||||||
|
CopyScope bool `json:"cs,omitempty"`
|
||||||
|
ThreadInNewTab bool `json:"tnt,omitempty"`
|
||||||
|
HideAttachments bool `json:"ha,omitempty"`
|
||||||
|
MaskNSFW bool `json:"mn,omitempty"`
|
||||||
|
NotificationInterval int `json:"ni,omitempty"`
|
||||||
|
FluorideMode bool `json:"fm,omitempty"`
|
||||||
|
DarkMode bool `json:"dm,omitempty"`
|
||||||
|
AntiDopamineMode bool `json:"adm,omitempty"`
|
||||||
|
HideUnsupportedNotifs bool `json:"hun,omitempty"`
|
||||||
|
InstanceEmojiFilter string `json:"iemojfilter,omitempty"`
|
||||||
|
AddReactionsFilter string `json:"reactionfilter,omitempty"`
|
||||||
|
CSS string `json:"css,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSettings() *Settings {
|
||||||
|
return &Settings{
|
||||||
|
DefaultVisibility: "public",
|
||||||
|
DefaultFormat: "",
|
||||||
|
CopyScope: true,
|
||||||
|
ThreadInNewTab: false,
|
||||||
|
HideAttachments: false,
|
||||||
|
MaskNSFW: true,
|
||||||
|
NotificationInterval: 0,
|
||||||
|
FluorideMode: false,
|
||||||
|
DarkMode: false,
|
||||||
|
AntiDopamineMode: false,
|
||||||
|
HideUnsupportedNotifs: false,
|
||||||
|
InstanceEmojiFilter: "",
|
||||||
|
AddReactionsFilter: "",
|
||||||
|
CSS: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
type Settings struct {
|
|
||||||
DefaultVisibility string `json:"default_visibility"`
|
|
||||||
DefaultFormat string `json:"default_format"`
|
|
||||||
CopyScope bool `json:"copy_scope"`
|
|
||||||
ThreadInNewTab bool `json:"thread_in_new_tab"`
|
|
||||||
HideAttachments bool `json:"hide_attachments"`
|
|
||||||
MaskNSFW bool `json:"mask_nfsw"`
|
|
||||||
NotificationInterval int `json:"notifications_interval"`
|
|
||||||
FluorideMode bool `json:"fluoride_mode"`
|
|
||||||
DarkMode bool `json:"dark_mode"`
|
|
||||||
AntiDopamineMode bool `json:"anti_dopamine_mode"`
|
|
||||||
HideUnsupportedNotifs bool `json:"hide_unsupported_notifs"`
|
|
||||||
InstanceEmojiFilter string `json:"instance_emoji_filter"`
|
|
||||||
AddReactionsFilter string `json:"add_reactions_filter"`
|
|
||||||
CSS string `json:"css"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSettings() *Settings {
|
|
||||||
return &Settings{
|
|
||||||
DefaultVisibility: "public",
|
|
||||||
DefaultFormat: "",
|
|
||||||
CopyScope: true,
|
|
||||||
ThreadInNewTab: false,
|
|
||||||
HideAttachments: false,
|
|
||||||
MaskNSFW: true,
|
|
||||||
NotificationInterval: 0,
|
|
||||||
FluorideMode: false,
|
|
||||||
DarkMode: false,
|
|
||||||
AntiDopamineMode: false,
|
|
||||||
HideUnsupportedNotifs: false,
|
|
||||||
InstanceEmojiFilter: "",
|
|
||||||
AddReactionsFilter: "",
|
|
||||||
CSS: "",
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package repo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"bloat/util"
|
|
||||||
"bloat/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type appRepo struct {
|
|
||||||
db *util.Database
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAppRepo(db *util.Database) *appRepo {
|
|
||||||
return &appRepo{
|
|
||||||
db: db,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *appRepo) Add(a model.App) (err error) {
|
|
||||||
data, err := json.Marshal(a)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = repo.db.Set(a.InstanceDomain, data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *appRepo) Get(instanceDomain string) (a model.App, err error) {
|
|
||||||
data, err := repo.db.Get(instanceDomain)
|
|
||||||
if err != nil {
|
|
||||||
err = model.ErrAppNotFound
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(data, &a)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package repo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"bloat/util"
|
|
||||||
"bloat/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type sessionRepo struct {
|
|
||||||
db *util.Database
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSessionRepo(db *util.Database) *sessionRepo {
|
|
||||||
return &sessionRepo{
|
|
||||||
db: db,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *sessionRepo) Add(s model.Session) (err error) {
|
|
||||||
data, err := json.Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = repo.db.Set(s.ID, data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *sessionRepo) Get(id string) (s model.Session, err error) {
|
|
||||||
data, err := repo.db.Get(id)
|
|
||||||
if err != nil {
|
|
||||||
err = model.ErrSessionNotFound
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(data, &s)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *sessionRepo) Remove(id string) {
|
|
||||||
repo.db.Remove(id)
|
|
||||||
return
|
|
||||||
}
|
|
113
service/client.go
Normal file
113
service/client.go
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"bloat/mastodon"
|
||||||
|
"bloat/model"
|
||||||
|
"bloat/renderer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
*mastodon.Client
|
||||||
|
w http.ResponseWriter
|
||||||
|
r *http.Request
|
||||||
|
s *model.Session
|
||||||
|
csrf string
|
||||||
|
ctx context.Context
|
||||||
|
rctx *renderer.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) setSession(sess *model.Session) error {
|
||||||
|
var sb strings.Builder
|
||||||
|
bw := base64.NewEncoder(base64.URLEncoding, &sb)
|
||||||
|
err := json.NewEncoder(bw).Encode(sess)
|
||||||
|
bw.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
http.SetCookie(c.w, &http.Cookie{
|
||||||
|
Name: "session",
|
||||||
|
Value: sb.String(),
|
||||||
|
Expires: time.Now().Add(365 * 24 * time.Hour),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) getSession() (sess *model.Session, err error) {
|
||||||
|
cookie, _ := c.r.Cookie("session")
|
||||||
|
if cookie == nil {
|
||||||
|
return nil, errInvalidSession
|
||||||
|
}
|
||||||
|
br := base64.NewDecoder(base64.URLEncoding, strings.NewReader(cookie.Value))
|
||||||
|
err = json.NewDecoder(br).Decode(&sess)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) unsetSession() {
|
||||||
|
http.SetCookie(c.w, &http.Cookie{
|
||||||
|
Name: "session",
|
||||||
|
Value: "",
|
||||||
|
Expires: time.Now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) writeJson(data interface{}) error {
|
||||||
|
return json.NewEncoder(c.w).Encode(map[string]interface{}{
|
||||||
|
"data": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) redirect(url string) {
|
||||||
|
c.w.Header().Add("Location", url)
|
||||||
|
c.w.WriteHeader(http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) authenticate(t int) (err error) {
|
||||||
|
csrf := c.r.FormValue("csrf_token")
|
||||||
|
ref := c.r.URL.RequestURI()
|
||||||
|
defer func() {
|
||||||
|
if c.s == nil {
|
||||||
|
c.s = &model.Session{
|
||||||
|
Settings: *model.NewSettings(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.rctx = &renderer.Context{
|
||||||
|
HideAttachments: c.s.Settings.HideAttachments,
|
||||||
|
MaskNSFW: c.s.Settings.MaskNSFW,
|
||||||
|
ThreadInNewTab: c.s.Settings.ThreadInNewTab,
|
||||||
|
FluorideMode: c.s.Settings.FluorideMode,
|
||||||
|
DarkMode: c.s.Settings.DarkMode,
|
||||||
|
CSRFToken: c.s.CSRFToken,
|
||||||
|
UserID: c.s.UserID,
|
||||||
|
AntiDopamineMode: c.s.Settings.AntiDopamineMode,
|
||||||
|
InstanceEmojiFilter: c.s.Settings.InstanceEmojiFilter,
|
||||||
|
AddReactionsFilter: c.s.Settings.AddReactionsFilter,
|
||||||
|
UserCSS: c.s.Settings.CSS,
|
||||||
|
Referrer: ref,
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if t < SESSION {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sess, err := c.getSession()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.s = sess
|
||||||
|
c.Client = mastodon.NewClient(&mastodon.Config{
|
||||||
|
Server: "https://" + c.s.Instance,
|
||||||
|
ClientID: c.s.ClientID,
|
||||||
|
ClientSecret: c.s.ClientSecret,
|
||||||
|
AccessToken: c.s.AccessToken,
|
||||||
|
})
|
||||||
|
if t >= CSRF && (len(csrf) < 1 || csrf != c.s.CSRFToken) {
|
||||||
|
return errInvalidCSRFToken
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -29,14 +29,11 @@ type service struct {
|
||||||
instance string
|
instance string
|
||||||
postFormats []model.PostFormat
|
postFormats []model.PostFormat
|
||||||
renderer renderer.Renderer
|
renderer renderer.Renderer
|
||||||
sessionRepo model.SessionRepo
|
|
||||||
appRepo model.AppRepo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(cname string, cscope string, cwebsite string,
|
func NewService(cname string, cscope string, cwebsite string,
|
||||||
css string, instance string, postFormats []model.PostFormat,
|
css string, instance string, postFormats []model.PostFormat,
|
||||||
renderer renderer.Renderer, sessionRepo model.SessionRepo,
|
renderer renderer.Renderer) *service {
|
||||||
appRepo model.AppRepo) *service {
|
|
||||||
return &service{
|
return &service{
|
||||||
cname: cname,
|
cname: cname,
|
||||||
cscope: cscope,
|
cscope: cscope,
|
||||||
|
@ -45,59 +42,9 @@ func NewService(cname string, cscope string, cwebsite string,
|
||||||
instance: instance,
|
instance: instance,
|
||||||
postFormats: postFormats,
|
postFormats: postFormats,
|
||||||
renderer: renderer,
|
renderer: renderer,
|
||||||
sessionRepo: sessionRepo,
|
|
||||||
appRepo: appRepo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) authenticate(c *client, sid string, csrf string, ref string, t int) (err error) {
|
|
||||||
var sett *model.Settings
|
|
||||||
defer func() {
|
|
||||||
if sett == nil {
|
|
||||||
sett = model.NewSettings()
|
|
||||||
}
|
|
||||||
c.rctx = &renderer.Context{
|
|
||||||
HideAttachments: sett.HideAttachments,
|
|
||||||
MaskNSFW: sett.MaskNSFW,
|
|
||||||
ThreadInNewTab: sett.ThreadInNewTab,
|
|
||||||
FluorideMode: sett.FluorideMode,
|
|
||||||
DarkMode: sett.DarkMode,
|
|
||||||
CSRFToken: c.s.CSRFToken,
|
|
||||||
UserID: c.s.UserID,
|
|
||||||
AntiDopamineMode: sett.AntiDopamineMode,
|
|
||||||
UserCSS: sett.CSS,
|
|
||||||
InstanceEmojiFilter: sett.InstanceEmojiFilter,
|
|
||||||
AddReactionsFilter: sett.AddReactionsFilter,
|
|
||||||
Referrer: ref,
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if t < SESSION {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(sid) < 1 {
|
|
||||||
return errInvalidSession
|
|
||||||
}
|
|
||||||
c.s, err = s.sessionRepo.Get(sid)
|
|
||||||
if err != nil {
|
|
||||||
return errInvalidSession
|
|
||||||
}
|
|
||||||
sett = &c.s.Settings
|
|
||||||
app, err := s.appRepo.Get(c.s.InstanceDomain)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Client = mastodon.NewClient(&mastodon.Config{
|
|
||||||
Server: app.InstanceURL,
|
|
||||||
ClientID: app.ClientID,
|
|
||||||
ClientSecret: app.ClientSecret,
|
|
||||||
AccessToken: c.s.AccessToken,
|
|
||||||
})
|
|
||||||
if t >= CSRF && (len(csrf) < 1 || csrf != c.s.CSRFToken) {
|
|
||||||
return errInvalidCSRFToken
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) cdata(c *client, title string, count int, rinterval int,
|
func (s *service) cdata(c *client, title string, count int, rinterval int,
|
||||||
target string) (data *renderer.CommonData) {
|
target string) (data *renderer.CommonData) {
|
||||||
data = &renderer.CommonData{
|
data = &renderer.CommonData{
|
||||||
|
@ -902,7 +849,7 @@ func (s *service) SingleInstance() (instance string, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) NewSession(c *client, instance string) (rurl string, sid string, err error) {
|
func (s *service) NewSession(c *client, instance string) (rurl string, sess *model.Session, err error) {
|
||||||
var instanceURL string
|
var instanceURL string
|
||||||
if strings.HasPrefix(instance, "https://") {
|
if strings.HasPrefix(instance, "https://") {
|
||||||
instanceURL = instance
|
instanceURL = instance
|
||||||
|
@ -911,7 +858,7 @@ func (s *service) NewSession(c *client, instance string) (rurl string, sid strin
|
||||||
instanceURL = "https://" + instance
|
instanceURL = "https://" + instance
|
||||||
}
|
}
|
||||||
|
|
||||||
sid, err = util.NewSessionID()
|
sid, err := util.NewSessionID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -920,42 +867,23 @@ func (s *service) NewSession(c *client, instance string) (rurl string, sid strin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := model.Session{
|
app, err := mastodon.RegisterApp(c.ctx, &mastodon.AppConfig{
|
||||||
ID: sid,
|
Server: instanceURL,
|
||||||
InstanceDomain: instance,
|
ClientName: s.cname,
|
||||||
CSRFToken: csrf,
|
Scopes: s.cscope,
|
||||||
Settings: *model.NewSettings(),
|
Website: s.cwebsite,
|
||||||
}
|
RedirectURIs: s.cwebsite + "/oauth_callback",
|
||||||
err = s.sessionRepo.Add(sess)
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
sess = &model.Session{
|
||||||
app, err := s.appRepo.Get(instance)
|
ID: sid,
|
||||||
if err != nil {
|
Instance: instance,
|
||||||
if err != model.ErrAppNotFound {
|
ClientID: app.ClientID,
|
||||||
return
|
ClientSecret: app.ClientSecret,
|
||||||
}
|
CSRFToken: csrf,
|
||||||
mastoApp, err := mastodon.RegisterApp(c.ctx, &mastodon.AppConfig{
|
Settings: *model.NewSettings(),
|
||||||
Server: instanceURL,
|
|
||||||
ClientName: s.cname,
|
|
||||||
Scopes: s.cscope,
|
|
||||||
Website: s.cwebsite,
|
|
||||||
RedirectURIs: s.cwebsite + "/oauth_callback",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
app = model.App{
|
|
||||||
InstanceDomain: instance,
|
|
||||||
InstanceURL: instanceURL,
|
|
||||||
ClientID: mastoApp.ClientID,
|
|
||||||
ClientSecret: mastoApp.ClientSecret,
|
|
||||||
}
|
|
||||||
err = s.appRepo.Add(app)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.Parse("/oauth/authorize")
|
u, err := url.Parse("/oauth/authorize")
|
||||||
|
@ -989,12 +917,7 @@ func (s *service) Signin(c *client, code string) (err error) {
|
||||||
}
|
}
|
||||||
c.s.AccessToken = c.GetAccessToken(c.ctx)
|
c.s.AccessToken = c.GetAccessToken(c.ctx)
|
||||||
c.s.UserID = u.ID
|
c.s.UserID = u.ID
|
||||||
return s.sessionRepo.Add(c.s)
|
return c.setSession(c.s)
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Signout(c *client) (err error) {
|
|
||||||
s.sessionRepo.Remove(c.s.ID)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Post(c *client, content string, replyToID string,
|
func (s *service) Post(c *client, content string, replyToID string,
|
||||||
|
@ -1146,12 +1069,8 @@ func (s *service) SaveSettings(c *client, settings *model.Settings) (err error)
|
||||||
if len(settings.CSS) > 1<<20 {
|
if len(settings.CSS) > 1<<20 {
|
||||||
return errInvalidArgument
|
return errInvalidArgument
|
||||||
}
|
}
|
||||||
sess, err := s.sessionRepo.Get(c.s.ID)
|
c.s.Settings = *settings
|
||||||
if err != nil {
|
return c.setSession(c.s)
|
||||||
return
|
|
||||||
}
|
|
||||||
sess.Settings = *settings
|
|
||||||
return s.sessionRepo.Add(sess)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) UserSave(c *client, usersettings mastodon.Profile) (err error) {
|
func (s *service) UserSave(c *client, usersettings mastodon.Profile) (err error) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -10,15 +9,10 @@ import (
|
||||||
|
|
||||||
"bloat/mastodon"
|
"bloat/mastodon"
|
||||||
"bloat/model"
|
"bloat/model"
|
||||||
"bloat/renderer"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
sessionExp = 365 * 24 * time.Hour
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HTML int = iota
|
HTML int = iota
|
||||||
JSON
|
JSON
|
||||||
|
@ -30,35 +24,6 @@ const (
|
||||||
CSRF
|
CSRF
|
||||||
)
|
)
|
||||||
|
|
||||||
type client struct {
|
|
||||||
*mastodon.Client
|
|
||||||
w http.ResponseWriter
|
|
||||||
r *http.Request
|
|
||||||
s model.Session
|
|
||||||
csrf string
|
|
||||||
ctx context.Context
|
|
||||||
rctx *renderer.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func setSessionCookie(w http.ResponseWriter, sid string, exp time.Duration) {
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
|
||||||
Name: "session_id",
|
|
||||||
Value: sid,
|
|
||||||
Expires: time.Now().Add(exp),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeJson(c *client, data interface{}) error {
|
|
||||||
return json.NewEncoder(c.w).Encode(map[string]interface{}{
|
|
||||||
"data": data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirect(c *client, url string) {
|
|
||||||
c.w.Header().Add("Location", url)
|
|
||||||
c.w.WriteHeader(http.StatusFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
|
@ -75,16 +40,6 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticate := func(c *client, t int) error {
|
|
||||||
var sid string
|
|
||||||
if cookie, _ := c.r.Cookie("session_id"); cookie != nil {
|
|
||||||
sid = cookie.Value
|
|
||||||
}
|
|
||||||
csrf := c.r.FormValue("csrf_token")
|
|
||||||
ref := c.r.URL.RequestURI()
|
|
||||||
return s.authenticate(c, sid, csrf, ref, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
handle := func(f func(c *client) error, at int, rt int) http.HandlerFunc {
|
handle := func(f func(c *client) error, at int, rt int) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, req *http.Request) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
|
@ -108,7 +63,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
}
|
}
|
||||||
c.w.Header().Add("Content-Type", ct)
|
c.w.Header().Add("Content-Type", ct)
|
||||||
|
|
||||||
err = authenticate(c, at)
|
err = c.authenticate(at)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(c, err, rt, req.Method == http.MethodGet)
|
writeError(c, err, rt, req.Method == http.MethodGet)
|
||||||
return
|
return
|
||||||
|
@ -123,16 +78,16 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
rootPage := handle(func(c *client) error {
|
rootPage := handle(func(c *client) error {
|
||||||
err := authenticate(c, SESSION)
|
err := c.authenticate(SESSION)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == errInvalidSession {
|
if err == errInvalidSession {
|
||||||
redirect(c, "/signin")
|
c.redirect("/signin")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !c.s.IsLoggedIn() {
|
if !c.s.IsLoggedIn() {
|
||||||
redirect(c, "/signin")
|
c.redirect("/signin")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return s.RootPage(c)
|
return s.RootPage(c)
|
||||||
|
@ -147,12 +102,12 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if !ok {
|
if !ok {
|
||||||
return s.SigninPage(c)
|
return s.SigninPage(c)
|
||||||
}
|
}
|
||||||
url, sid, err := s.NewSession(c, instance)
|
url, sess, err := s.NewSession(c, instance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
setSessionCookie(c.w, sid, sessionExp)
|
c.setSession(sess)
|
||||||
redirect(c, url)
|
c.redirect(url)
|
||||||
return nil
|
return nil
|
||||||
}, NOAUTH, HTML)
|
}, NOAUTH, HTML)
|
||||||
|
|
||||||
|
@ -168,7 +123,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
}, SESSION, HTML)
|
}, SESSION, HTML)
|
||||||
|
|
||||||
defaultTimelinePage := handle(func(c *client) error {
|
defaultTimelinePage := handle(func(c *client) error {
|
||||||
redirect(c, "/timeline/home")
|
c.redirect("/timeline/home")
|
||||||
return nil
|
return nil
|
||||||
}, SESSION, HTML)
|
}, SESSION, HTML)
|
||||||
|
|
||||||
|
@ -276,7 +231,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, "/user/"+c.r.FormValue("id"))
|
c.redirect("/user/"+c.r.FormValue("id"))
|
||||||
return nil
|
return nil
|
||||||
}, SESSION, HTML)
|
}, SESSION, HTML)
|
||||||
|
|
||||||
|
@ -290,12 +245,12 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
|
|
||||||
signin := handle(func(c *client) error {
|
signin := handle(func(c *client) error {
|
||||||
instance := c.r.FormValue("instance")
|
instance := c.r.FormValue("instance")
|
||||||
url, sid, err := s.NewSession(c, instance)
|
url, sess, err := s.NewSession(c, instance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
setSessionCookie(c.w, sid, sessionExp)
|
c.setSession(sess)
|
||||||
redirect(c, url)
|
c.redirect(url)
|
||||||
return nil
|
return nil
|
||||||
}, NOAUTH, HTML)
|
}, NOAUTH, HTML)
|
||||||
|
|
||||||
|
@ -306,7 +261,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, "/")
|
c.redirect("/")
|
||||||
return nil
|
return nil
|
||||||
}, SESSION, HTML)
|
}, SESSION, HTML)
|
||||||
|
|
||||||
|
@ -336,7 +291,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
} else {
|
} else {
|
||||||
location = c.r.FormValue("referrer")
|
location = c.r.FormValue("referrer")
|
||||||
}
|
}
|
||||||
redirect(c, location)
|
c.redirect(location)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -350,7 +305,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if len(rid) > 0 {
|
if len(rid) > 0 {
|
||||||
id = rid
|
id = rid
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer") + "#status-" + id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -364,7 +319,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if len(rid) > 0 {
|
if len(rid) > 0 {
|
||||||
id = rid
|
id = rid
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer") + "#status-" + id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -380,7 +335,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer")+"#status-"+id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -392,7 +347,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer")+"#status-"+id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -407,7 +362,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if len(rid) > 0 {
|
if len(rid) > 0 {
|
||||||
id = rid
|
id = rid
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer") + "#status-" + id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -421,7 +376,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if len(rid) > 0 {
|
if len(rid) > 0 {
|
||||||
id = rid
|
id = rid
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer") + "#status-" + id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -433,7 +388,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+statusID)
|
c.redirect(c.r.FormValue("referrer") + "#status-" + statusID)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -449,7 +404,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -459,7 +414,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -469,7 +424,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -479,7 +434,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -495,7 +450,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -505,7 +460,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -515,7 +470,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -525,7 +480,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -535,7 +490,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -545,7 +500,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -586,7 +541,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, "/")
|
c.redirect("/")
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -596,7 +551,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -606,7 +561,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -616,7 +571,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -626,7 +581,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -636,7 +591,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -647,7 +602,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -661,7 +616,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if len(rid) > 0 {
|
if len(rid) > 0 {
|
||||||
id = rid
|
id = rid
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer") + "#status-" + id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -675,7 +630,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if len(rid) > 0 {
|
if len(rid) > 0 {
|
||||||
id = rid
|
id = rid
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer")+"#status-"+id)
|
c.redirect(c.r.FormValue("referrer") + "#status-" + id)
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -686,7 +641,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -696,7 +651,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -710,7 +665,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -720,7 +675,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -731,7 +686,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -750,7 +705,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -762,14 +717,13 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirect(c, c.r.FormValue("referrer"))
|
c.redirect(c.r.FormValue("referrer"))
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
signout := handle(func(c *client) error {
|
signout := handle(func(c *client) error {
|
||||||
s.Signout(c)
|
c.unsetSession()
|
||||||
setSessionCookie(c.w, "", 0)
|
c.redirect("/")
|
||||||
redirect(c, "/")
|
|
||||||
return nil
|
return nil
|
||||||
}, CSRF, HTML)
|
}, CSRF, HTML)
|
||||||
|
|
||||||
|
@ -779,7 +733,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return writeJson(c, count)
|
return c.writeJson(count)
|
||||||
}, CSRF, JSON)
|
}, CSRF, JSON)
|
||||||
|
|
||||||
fUnlike := handle(func(c *client) error {
|
fUnlike := handle(func(c *client) error {
|
||||||
|
@ -788,7 +742,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return writeJson(c, count)
|
return c.writeJson(count)
|
||||||
}, CSRF, JSON)
|
}, CSRF, JSON)
|
||||||
|
|
||||||
fRetweet := handle(func(c *client) error {
|
fRetweet := handle(func(c *client) error {
|
||||||
|
@ -797,7 +751,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return writeJson(c, count)
|
return c.writeJson(count)
|
||||||
}, CSRF, JSON)
|
}, CSRF, JSON)
|
||||||
|
|
||||||
fUnretweet := handle(func(c *client) error {
|
fUnretweet := handle(func(c *client) error {
|
||||||
|
@ -806,7 +760,7 @@ func NewHandler(s *service, logger *log.Logger, staticDir string) http.Handler {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return writeJson(c, count)
|
return c.writeJson(count)
|
||||||
}, CSRF, JSON)
|
}, CSRF, JSON)
|
||||||
|
|
||||||
r.HandleFunc("/", rootPage).Methods(http.MethodGet)
|
r.HandleFunc("/", rootPage).Methods(http.MethodGet)
|
||||||
|
|
48
templates/useredit.tmpl
Normal file
48
templates/useredit.tmpl
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{{with .Data}}
|
||||||
|
{{template "header.tmpl" (WithContext .CommonData $.Ctx)}}
|
||||||
|
<div class="page-title"> User settings </div>
|
||||||
|
|
||||||
|
<form id="user-settings-form" action="/useredit" method="POST">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{$.Ctx.CSRFToken}}">
|
||||||
|
<input type="hidden" name="referrer" value="{{$.Ctx.Referrer}}">
|
||||||
|
<input type="hidden" name="id" value="{{.User.ID}}">
|
||||||
|
<div class="settings-form-field">
|
||||||
|
<input id="display-name" name="display-name" value="{{HTML .User.DisplayName}}">
|
||||||
|
<label for="display-name"> Your display name </label>
|
||||||
|
</div>
|
||||||
|
<div class="settings-form-field">
|
||||||
|
<label for="note"> Your bio: </label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<textarea id="note" name="note" cols="80" rows="8"></textarea>
|
||||||
|
<div>
|
||||||
|
<details><summary>Current bio for copypaste</summary>{{.User.Note | Raw}}</details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-form-field" title="Whether manual approval of follow requests is required.">
|
||||||
|
<input id="locked" name="locked" type="checkbox" value="true" {{if .User.Locked}}checked{{end}}>
|
||||||
|
<label for="locked"> Locked user? </label>
|
||||||
|
</div>
|
||||||
|
<div class="settings-form-field" title="Whether manual approval of follow requests is required.">
|
||||||
|
<input id="bot" name="bot" type="checkbox" value="true" {{if .User.Bot}}checked{{end}}>
|
||||||
|
<label for="bot"> User is bot? </label>
|
||||||
|
</div>
|
||||||
|
{{if .User.MastodonAccount}}
|
||||||
|
{{else}}
|
||||||
|
<div class="page-title"> Pleroma settings </div>
|
||||||
|
|
||||||
|
<div class="settings-form-field">
|
||||||
|
<input id="accepts-chat-messages" name="hide-attachments" type="checkbox" value="true" {{if .User.Pleroma.AcceptsChatMessages}}checked{{end}}>
|
||||||
|
<label for="accepts-chat-messages"> Allow receive chat messages (bloat not support chats feature) </label>
|
||||||
|
</div>
|
||||||
|
<div class="settings-form-field">
|
||||||
|
<input id="hide-favourites" name="hide-favourites" type="checkbox" value="true" {{if .User.Pleroma.HideFavourites}}checked{{end}}>
|
||||||
|
<label for="hide-favourites"> User's favorites timeline will be hidden </label>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<button type="submit"> Save </button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{template "footer.tmpl"}}
|
||||||
|
{{end}}
|
91
util/kv.go
91
util/kv.go
|
@ -1,91 +0,0 @@
|
||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errInvalidKey = errors.New("invalid key")
|
|
||||||
errNoSuchKey = errors.New("no such key")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Database struct {
|
|
||||||
cache map[string][]byte
|
|
||||||
basedir string
|
|
||||||
m sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDatabse(basedir string) (db *Database, err error) {
|
|
||||||
err = os.Mkdir(basedir, 0755)
|
|
||||||
if err != nil && !os.IsExist(err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Database{
|
|
||||||
cache: make(map[string][]byte),
|
|
||||||
basedir: basedir,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) Set(key string, val []byte) (err error) {
|
|
||||||
if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) {
|
|
||||||
return errInvalidKey
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(filepath.Join(db.basedir, key), val, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
db.m.Lock()
|
|
||||||
db.cache[key] = val
|
|
||||||
db.m.Unlock()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) Get(key string) (val []byte, err error) {
|
|
||||||
if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) {
|
|
||||||
return nil, errInvalidKey
|
|
||||||
}
|
|
||||||
|
|
||||||
db.m.RLock()
|
|
||||||
data, ok := db.cache[key]
|
|
||||||
db.m.RUnlock()
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
data, err = ioutil.ReadFile(filepath.Join(db.basedir, key))
|
|
||||||
if err != nil {
|
|
||||||
err = errNoSuchKey
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
db.m.Lock()
|
|
||||||
db.cache[key] = data
|
|
||||||
db.m.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
val = make([]byte, len(data))
|
|
||||||
copy(val, data)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) Remove(key string) {
|
|
||||||
if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Remove(filepath.Join(db.basedir, key))
|
|
||||||
|
|
||||||
db.m.Lock()
|
|
||||||
delete(db.cache, key)
|
|
||||||
db.m.Unlock()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
Loading…
Reference in a new issue