Page Menu
Home
Musing Studio
Search
Configure Global Search
Log In
Files
F10384149
admin.go
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Subscribers
None
admin.go
View Options
/*
* Copyright © 2018-2021 A Bunch Tell LLC.
*
* This file is part of WriteFreely.
*
* WriteFreely is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, included
* in the LICENSE file in this source code package.
*/
package
writefreely
import
(
"database/sql"
"fmt"
"net/http"
"runtime"
"strconv"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/writeas/impart"
"github.com/writeas/web-core/auth"
"github.com/writeas/web-core/log"
"github.com/writeas/web-core/passgen"
"github.com/writefreely/writefreely/appstats"
"github.com/writefreely/writefreely/config"
)
var
(
appStartTime
=
time
.
Now
()
sysStatus
systemStatus
)
const
adminUsersPerPage
=
30
type
systemStatus
struct
{
Uptime
string
NumGoroutine
int
// General statistics.
MemAllocated
string
// bytes allocated and still in use
MemTotal
string
// bytes allocated (even if freed)
MemSys
string
// bytes obtained from system (sum of XxxSys below)
Lookups
uint64
// number of pointer lookups
MemMallocs
uint64
// number of mallocs
MemFrees
uint64
// number of frees
// Main allocation heap statistics.
HeapAlloc
string
// bytes allocated and still in use
HeapSys
string
// bytes obtained from system
HeapIdle
string
// bytes in idle spans
HeapInuse
string
// bytes in non-idle span
HeapReleased
string
// bytes released to the OS
HeapObjects
uint64
// total number of allocated objects
// Low-level fixed-size structure allocator statistics.
// Inuse is bytes used now.
// Sys is bytes obtained from system.
StackInuse
string
// bootstrap stacks
StackSys
string
MSpanInuse
string
// mspan structures
MSpanSys
string
MCacheInuse
string
// mcache structures
MCacheSys
string
BuckHashSys
string
// profiling bucket hash table
GCSys
string
// GC metadata
OtherSys
string
// other system allocations
// Garbage collector statistics.
NextGC
string
// next run in HeapAlloc time (bytes)
LastGC
string
// last run in absolute time (ns)
PauseTotalNs
string
PauseNs
string
// circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
NumGC
uint32
}
type
inspectedCollection
struct
{
CollectionObj
Followers
int
LastPost
string
}
type
instanceContent
struct
{
ID
string
Type
string
Title
sql
.
NullString
Content
string
Updated
time
.
Time
}
type
AdminPage
struct
{
UpdateAvailable
bool
}
func
NewAdminPage
(
app
*
App
)
*
AdminPage
{
ap
:=
&
AdminPage
{}
if
app
.
updates
!=
nil
{
ap
.
UpdateAvailable
=
app
.
updates
.
AreAvailableNoCheck
()
}
return
ap
}
func
(
c
instanceContent
)
UpdatedFriendly
()
string
{
/*
// TODO: accept a locale in this method and use that for the format
var loc monday.Locale = monday.LocaleEnUS
return monday.Format(u.Created, monday.DateTimeFormatsByLocale[loc], loc)
*/
return
c
.
Updated
.
Format
(
"January 2, 2006, 3:04 PM"
)
}
func
handleViewAdminDash
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
p
:=
struct
{
*
UserPage
*
AdminPage
Message
string
UsersCount
,
CollectionsCount
,
PostsCount
int64
}{
UserPage
:
NewUserPage
(
app
,
r
,
u
,
"Admin"
,
nil
),
AdminPage
:
NewAdminPage
(
app
),
Message
:
r
.
FormValue
(
"m"
),
}
// Get user stats
p
.
UsersCount
=
app
.
db
.
GetAllUsersCount
()
var
err
error
p
.
CollectionsCount
,
err
=
app
.
db
.
GetTotalCollections
()
if
err
!=
nil
{
return
err
}
p
.
PostsCount
,
err
=
app
.
db
.
GetTotalPosts
()
if
err
!=
nil
{
return
err
}
showUserPage
(
w
,
"admin"
,
p
)
return
nil
}
func
handleViewAdminMonitor
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
updateAppStats
()
p
:=
struct
{
*
UserPage
*
AdminPage
SysStatus
systemStatus
Config
config
.
AppCfg
Message
,
ConfigMessage
string
}{
UserPage
:
NewUserPage
(
app
,
r
,
u
,
"Admin"
,
nil
),
AdminPage
:
NewAdminPage
(
app
),
SysStatus
:
sysStatus
,
Config
:
app
.
cfg
.
App
,
Message
:
r
.
FormValue
(
"m"
),
ConfigMessage
:
r
.
FormValue
(
"cm"
),
}
showUserPage
(
w
,
"monitor"
,
p
)
return
nil
}
func
handleViewAdminSettings
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
p
:=
struct
{
*
UserPage
*
AdminPage
Config
config
.
AppCfg
Message
,
ConfigMessage
string
}{
UserPage
:
NewUserPage
(
app
,
r
,
u
,
"Admin"
,
nil
),
AdminPage
:
NewAdminPage
(
app
),
Config
:
app
.
cfg
.
App
,
Message
:
r
.
FormValue
(
"m"
),
ConfigMessage
:
r
.
FormValue
(
"cm"
),
}
showUserPage
(
w
,
"app-settings"
,
p
)
return
nil
}
func
handleViewAdminUsers
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
p
:=
struct
{
*
UserPage
*
AdminPage
Config
config
.
AppCfg
Message
string
Flashes
[]
string
Users
*
[]
User
CurPage
int
TotalUsers
int64
TotalPages
[]
int
}{
UserPage
:
NewUserPage
(
app
,
r
,
u
,
"Users"
,
nil
),
AdminPage
:
NewAdminPage
(
app
),
Config
:
app
.
cfg
.
App
,
Message
:
r
.
FormValue
(
"m"
),
}
p
.
Flashes
,
_
=
getSessionFlashes
(
app
,
w
,
r
,
nil
)
p
.
TotalUsers
=
app
.
db
.
GetAllUsersCount
()
ttlPages
:=
p
.
TotalUsers
/
adminUsersPerPage
p
.
TotalPages
=
[]
int
{}
for
i
:=
1
;
i
<=
int
(
ttlPages
);
i
++
{
p
.
TotalPages
=
append
(
p
.
TotalPages
,
i
)
}
var
err
error
p
.
CurPage
,
err
=
strconv
.
Atoi
(
r
.
FormValue
(
"p"
))
if
err
!=
nil
||
p
.
CurPage
<
1
{
p
.
CurPage
=
1
}
else
if
p
.
CurPage
>
int
(
ttlPages
)
{
p
.
CurPage
=
int
(
ttlPages
)
}
p
.
Users
,
err
=
app
.
db
.
GetAllUsers
(
uint
(
p
.
CurPage
))
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get users: %v"
,
err
)}
}
showUserPage
(
w
,
"users"
,
p
)
return
nil
}
func
handleViewAdminUser
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
vars
:=
mux
.
Vars
(
r
)
username
:=
vars
[
"username"
]
if
username
==
""
{
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/users"
}
}
p
:=
struct
{
*
UserPage
*
AdminPage
Config
config
.
AppCfg
Message
string
User
*
User
Colls
[]
inspectedCollection
LastPost
string
NewPassword
string
TotalPosts
int64
ClearEmail
string
}{
AdminPage
:
NewAdminPage
(
app
),
Config
:
app
.
cfg
.
App
,
Message
:
r
.
FormValue
(
"m"
),
Colls
:
[]
inspectedCollection
{},
}
var
err
error
p
.
User
,
err
=
app
.
db
.
GetUserForAuth
(
username
)
if
err
!=
nil
{
if
err
==
ErrUserNotFound
{
return
err
}
log
.
Error
(
"Could not get user: %v"
,
err
)
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
err
.
Error
()}
}
flashes
,
_
:=
getSessionFlashes
(
app
,
w
,
r
,
nil
)
for
_
,
flash
:=
range
flashes
{
if
strings
.
HasPrefix
(
flash
,
"SUCCESS: "
)
{
p
.
NewPassword
=
strings
.
TrimPrefix
(
flash
,
"SUCCESS: "
)
p
.
ClearEmail
=
p
.
User
.
EmailClear
(
app
.
keys
)
}
}
p
.
UserPage
=
NewUserPage
(
app
,
r
,
u
,
p
.
User
.
Username
,
nil
)
p
.
TotalPosts
=
app
.
db
.
GetUserPostsCount
(
p
.
User
.
ID
)
lp
,
err
:=
app
.
db
.
GetUserLastPostTime
(
p
.
User
.
ID
)
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get user's last post time: %v"
,
err
)}
}
if
lp
!=
nil
{
p
.
LastPost
=
lp
.
Format
(
"January 2, 2006, 3:04 PM"
)
}
colls
,
err
:=
app
.
db
.
GetCollections
(
p
.
User
,
app
.
cfg
.
App
.
Host
)
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get user's collections: %v"
,
err
)}
}
for
_
,
c
:=
range
*
colls
{
ic
:=
inspectedCollection
{
CollectionObj
:
CollectionObj
{
Collection
:
c
},
}
if
app
.
cfg
.
App
.
Federation
{
folls
,
err
:=
app
.
db
.
GetAPFollowers
(
&
c
)
if
err
==
nil
{
// TODO: handle error here (at least log it)
ic
.
Followers
=
len
(
*
folls
)
}
}
app
.
db
.
GetPostsCount
(
&
ic
.
CollectionObj
,
true
)
lp
,
err
:=
app
.
db
.
GetCollectionLastPostTime
(
c
.
ID
)
if
err
!=
nil
{
log
.
Error
(
"Didn't get last post time for collection %d: %v"
,
c
.
ID
,
err
)
}
if
lp
!=
nil
{
ic
.
LastPost
=
lp
.
Format
(
"January 2, 2006, 3:04 PM"
)
}
p
.
Colls
=
append
(
p
.
Colls
,
ic
)
}
showUserPage
(
w
,
"view-user"
,
p
)
return
nil
}
func
handleAdminDeleteUser
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
if
!
u
.
IsAdmin
()
{
return
impart
.
HTTPError
{
http
.
StatusForbidden
,
"Administrator privileges required for this action"
}
}
vars
:=
mux
.
Vars
(
r
)
username
:=
vars
[
"username"
]
confirmUsername
:=
r
.
PostFormValue
(
"confirm-username"
)
if
confirmUsername
!=
username
{
return
impart
.
HTTPError
{
http
.
StatusBadRequest
,
"Username was not confirmed"
}
}
user
,
err
:=
app
.
db
.
GetUserForAuth
(
username
)
if
err
==
ErrUserNotFound
{
return
impart
.
HTTPError
{
http
.
StatusNotFound
,
fmt
.
Sprintf
(
"User '%s' was not found"
,
username
)}
}
else
if
err
!=
nil
{
log
.
Error
(
"get user for deletion: %v"
,
err
)
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get user with username '%s': %v"
,
username
,
err
)}
}
err
=
app
.
db
.
DeleteAccount
(
user
.
ID
)
if
err
!=
nil
{
log
.
Error
(
"delete user %s: %v"
,
user
.
Username
,
err
)
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not delete user account for '%s': %v"
,
username
,
err
)}
}
_
=
addSessionFlash
(
app
,
w
,
r
,
fmt
.
Sprintf
(
"User \"%s\" was deleted successfully."
,
username
),
nil
)
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/users"
}
}
func
handleAdminToggleUserStatus
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
vars
:=
mux
.
Vars
(
r
)
username
:=
vars
[
"username"
]
if
username
==
""
{
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/users"
}
}
user
,
err
:=
app
.
db
.
GetUserForAuth
(
username
)
if
err
!=
nil
{
log
.
Error
(
"failed to get user: %v"
,
err
)
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get user from username: %v"
,
err
)}
}
if
user
.
IsSilenced
()
{
err
=
app
.
db
.
SetUserStatus
(
user
.
ID
,
UserActive
)
}
else
{
err
=
app
.
db
.
SetUserStatus
(
user
.
ID
,
UserSilenced
)
// reset the cache to removed silence user posts
updateTimelineCache
(
app
.
timeline
,
true
)
}
if
err
!=
nil
{
log
.
Error
(
"toggle user silenced: %v"
,
err
)
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not toggle user status: %v"
,
err
)}
}
return
impart
.
HTTPError
{
http
.
StatusFound
,
fmt
.
Sprintf
(
"/admin/user/%s#status"
,
username
)}
}
func
handleAdminResetUserPass
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
vars
:=
mux
.
Vars
(
r
)
username
:=
vars
[
"username"
]
if
username
==
""
{
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/users"
}
}
// Generate new random password since none supplied
pass
:=
passgen
.
NewWordish
()
hashedPass
,
err
:=
auth
.
HashPass
([]
byte
(
pass
))
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not create password hash: %v"
,
err
)}
}
userIDVal
:=
r
.
FormValue
(
"user"
)
log
.
Info
(
"ADMIN: Changing user %s password"
,
userIDVal
)
id
,
err
:=
strconv
.
Atoi
(
userIDVal
)
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusBadRequest
,
fmt
.
Sprintf
(
"Invalid user ID: %v"
,
err
)}
}
err
=
app
.
db
.
ChangePassphrase
(
int64
(
id
),
true
,
""
,
hashedPass
)
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not update passphrase: %v"
,
err
)}
}
log
.
Info
(
"ADMIN: Successfully changed."
)
addSessionFlash
(
app
,
w
,
r
,
fmt
.
Sprintf
(
"SUCCESS: %s"
,
pass
),
nil
)
return
impart
.
HTTPError
{
http
.
StatusFound
,
fmt
.
Sprintf
(
"/admin/user/%s"
,
username
)}
}
func
handleViewAdminPages
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
p
:=
struct
{
*
UserPage
*
AdminPage
Config
config
.
AppCfg
Message
string
Pages
[]
*
instanceContent
}{
UserPage
:
NewUserPage
(
app
,
r
,
u
,
"Pages"
,
nil
),
AdminPage
:
NewAdminPage
(
app
),
Config
:
app
.
cfg
.
App
,
Message
:
r
.
FormValue
(
"m"
),
}
var
err
error
p
.
Pages
,
err
=
app
.
db
.
GetInstancePages
()
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get pages: %v"
,
err
)}
}
// Add in default pages
var
hasAbout
,
hasPrivacy
bool
for
i
,
c
:=
range
p
.
Pages
{
if
hasAbout
&&
hasPrivacy
{
break
}
if
c
.
ID
==
"about"
{
hasAbout
=
true
if
!
c
.
Title
.
Valid
{
p
.
Pages
[
i
].
Title
=
defaultAboutTitle
(
app
.
cfg
)
}
}
else
if
c
.
ID
==
"privacy"
{
hasPrivacy
=
true
if
!
c
.
Title
.
Valid
{
p
.
Pages
[
i
].
Title
=
defaultPrivacyTitle
()
}
}
}
if
!
hasAbout
{
p
.
Pages
=
append
(
p
.
Pages
,
&
instanceContent
{
ID
:
"about"
,
Title
:
defaultAboutTitle
(
app
.
cfg
),
Content
:
defaultAboutPage
(
app
.
cfg
),
Updated
:
defaultPageUpdatedTime
,
})
}
if
!
hasPrivacy
{
p
.
Pages
=
append
(
p
.
Pages
,
&
instanceContent
{
ID
:
"privacy"
,
Title
:
defaultPrivacyTitle
(),
Content
:
defaultPrivacyPolicy
(
app
.
cfg
),
Updated
:
defaultPageUpdatedTime
,
})
}
showUserPage
(
w
,
"pages"
,
p
)
return
nil
}
func
handleViewAdminPage
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
vars
:=
mux
.
Vars
(
r
)
slug
:=
vars
[
"slug"
]
if
slug
==
""
{
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/pages"
}
}
p
:=
struct
{
*
UserPage
*
AdminPage
Config
config
.
AppCfg
Message
string
Banner
*
instanceContent
Content
*
instanceContent
}{
AdminPage
:
NewAdminPage
(
app
),
Config
:
app
.
cfg
.
App
,
Message
:
r
.
FormValue
(
"m"
),
}
var
err
error
// Get pre-defined pages, or select slug
if
slug
==
"about"
{
p
.
Content
,
err
=
getAboutPage
(
app
)
}
else
if
slug
==
"privacy"
{
p
.
Content
,
err
=
getPrivacyPage
(
app
)
}
else
if
slug
==
"landing"
{
p
.
Banner
,
err
=
getLandingBanner
(
app
)
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get banner: %v"
,
err
)}
}
p
.
Content
,
err
=
getLandingBody
(
app
)
p
.
Content
.
ID
=
"landing"
}
else
if
slug
==
"reader"
{
p
.
Content
,
err
=
getReaderSection
(
app
)
}
else
{
p
.
Content
,
err
=
app
.
db
.
GetDynamicContent
(
slug
)
}
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not get page: %v"
,
err
)}
}
title
:=
"New page"
if
p
.
Content
!=
nil
{
title
=
"Edit "
+
p
.
Content
.
ID
}
else
{
p
.
Content
=
&
instanceContent
{}
}
p
.
UserPage
=
NewUserPage
(
app
,
r
,
u
,
title
,
nil
)
showUserPage
(
w
,
"view-page"
,
p
)
return
nil
}
func
handleAdminUpdateSite
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
vars
:=
mux
.
Vars
(
r
)
id
:=
vars
[
"page"
]
// Validate
if
id
!=
"about"
&&
id
!=
"privacy"
&&
id
!=
"landing"
&&
id
!=
"reader"
{
return
impart
.
HTTPError
{
http
.
StatusNotFound
,
"No such page."
}
}
var
err
error
m
:=
""
if
id
==
"landing"
{
// Handle special landing page
err
=
app
.
db
.
UpdateDynamicContent
(
"landing-banner"
,
""
,
r
.
FormValue
(
"banner"
),
"section"
)
if
err
!=
nil
{
m
=
"?m="
+
err
.
Error
()
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/page/"
+
id
+
m
}
}
err
=
app
.
db
.
UpdateDynamicContent
(
"landing-body"
,
""
,
r
.
FormValue
(
"content"
),
"section"
)
}
else
if
id
==
"reader"
{
// Update sections with titles
err
=
app
.
db
.
UpdateDynamicContent
(
id
,
r
.
FormValue
(
"title"
),
r
.
FormValue
(
"content"
),
"section"
)
}
else
{
// Update page
err
=
app
.
db
.
UpdateDynamicContent
(
id
,
r
.
FormValue
(
"title"
),
r
.
FormValue
(
"content"
),
"page"
)
}
if
err
!=
nil
{
m
=
"?m="
+
err
.
Error
()
}
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/page/"
+
id
+
m
}
}
func
handleAdminUpdateConfig
(
apper
Apper
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
apper
.
App
().
cfg
.
App
.
SiteName
=
r
.
FormValue
(
"site_name"
)
apper
.
App
().
cfg
.
App
.
SiteDesc
=
r
.
FormValue
(
"site_desc"
)
apper
.
App
().
cfg
.
App
.
Landing
=
r
.
FormValue
(
"landing"
)
apper
.
App
().
cfg
.
App
.
OpenRegistration
=
r
.
FormValue
(
"open_registration"
)
==
"on"
apper
.
App
().
cfg
.
App
.
OpenDeletion
=
r
.
FormValue
(
"open_deletion"
)
==
"on"
mul
,
err
:=
strconv
.
Atoi
(
r
.
FormValue
(
"min_username_len"
))
if
err
==
nil
{
apper
.
App
().
cfg
.
App
.
MinUsernameLen
=
mul
}
mb
,
err
:=
strconv
.
Atoi
(
r
.
FormValue
(
"max_blogs"
))
if
err
==
nil
{
apper
.
App
().
cfg
.
App
.
MaxBlogs
=
mb
}
apper
.
App
().
cfg
.
App
.
Federation
=
r
.
FormValue
(
"federation"
)
==
"on"
apper
.
App
().
cfg
.
App
.
PublicStats
=
r
.
FormValue
(
"public_stats"
)
==
"on"
apper
.
App
().
cfg
.
App
.
Monetization
=
r
.
FormValue
(
"monetization"
)
==
"on"
apper
.
App
().
cfg
.
App
.
Private
=
r
.
FormValue
(
"private"
)
==
"on"
apper
.
App
().
cfg
.
App
.
LocalTimeline
=
r
.
FormValue
(
"local_timeline"
)
==
"on"
if
apper
.
App
().
cfg
.
App
.
LocalTimeline
&&
apper
.
App
().
timeline
==
nil
{
log
.
Info
(
"Initializing local timeline..."
)
initLocalTimeline
(
apper
.
App
())
}
apper
.
App
().
cfg
.
App
.
UserInvites
=
r
.
FormValue
(
"user_invites"
)
if
apper
.
App
().
cfg
.
App
.
UserInvites
==
"none"
{
apper
.
App
().
cfg
.
App
.
UserInvites
=
""
}
apper
.
App
().
cfg
.
App
.
DefaultVisibility
=
r
.
FormValue
(
"default_visibility"
)
m
:=
"?cm=Configuration+saved."
err
=
apper
.
SaveConfig
(
apper
.
App
().
cfg
)
if
err
!=
nil
{
m
=
"?cm="
+
err
.
Error
()
}
return
impart
.
HTTPError
{
http
.
StatusFound
,
"/admin/settings"
+
m
+
"#config"
}
}
func
updateAppStats
()
{
sysStatus
.
Uptime
=
appstats
.
TimeSincePro
(
appStartTime
)
m
:=
new
(
runtime
.
MemStats
)
runtime
.
ReadMemStats
(
m
)
sysStatus
.
NumGoroutine
=
runtime
.
NumGoroutine
()
sysStatus
.
MemAllocated
=
appstats
.
FileSize
(
int64
(
m
.
Alloc
))
sysStatus
.
MemTotal
=
appstats
.
FileSize
(
int64
(
m
.
TotalAlloc
))
sysStatus
.
MemSys
=
appstats
.
FileSize
(
int64
(
m
.
Sys
))
sysStatus
.
Lookups
=
m
.
Lookups
sysStatus
.
MemMallocs
=
m
.
Mallocs
sysStatus
.
MemFrees
=
m
.
Frees
sysStatus
.
HeapAlloc
=
appstats
.
FileSize
(
int64
(
m
.
HeapAlloc
))
sysStatus
.
HeapSys
=
appstats
.
FileSize
(
int64
(
m
.
HeapSys
))
sysStatus
.
HeapIdle
=
appstats
.
FileSize
(
int64
(
m
.
HeapIdle
))
sysStatus
.
HeapInuse
=
appstats
.
FileSize
(
int64
(
m
.
HeapInuse
))
sysStatus
.
HeapReleased
=
appstats
.
FileSize
(
int64
(
m
.
HeapReleased
))
sysStatus
.
HeapObjects
=
m
.
HeapObjects
sysStatus
.
StackInuse
=
appstats
.
FileSize
(
int64
(
m
.
StackInuse
))
sysStatus
.
StackSys
=
appstats
.
FileSize
(
int64
(
m
.
StackSys
))
sysStatus
.
MSpanInuse
=
appstats
.
FileSize
(
int64
(
m
.
MSpanInuse
))
sysStatus
.
MSpanSys
=
appstats
.
FileSize
(
int64
(
m
.
MSpanSys
))
sysStatus
.
MCacheInuse
=
appstats
.
FileSize
(
int64
(
m
.
MCacheInuse
))
sysStatus
.
MCacheSys
=
appstats
.
FileSize
(
int64
(
m
.
MCacheSys
))
sysStatus
.
BuckHashSys
=
appstats
.
FileSize
(
int64
(
m
.
BuckHashSys
))
sysStatus
.
GCSys
=
appstats
.
FileSize
(
int64
(
m
.
GCSys
))
sysStatus
.
OtherSys
=
appstats
.
FileSize
(
int64
(
m
.
OtherSys
))
sysStatus
.
NextGC
=
appstats
.
FileSize
(
int64
(
m
.
NextGC
))
sysStatus
.
LastGC
=
fmt
.
Sprintf
(
"%.1fs"
,
float64
(
time
.
Now
().
UnixNano
()
-
int64
(
m
.
LastGC
))
/
1000
/
1000
/
1000
)
sysStatus
.
PauseTotalNs
=
fmt
.
Sprintf
(
"%.1fs"
,
float64
(
m
.
PauseTotalNs
)
/
1000
/
1000
/
1000
)
sysStatus
.
PauseNs
=
fmt
.
Sprintf
(
"%.3fs"
,
float64
(
m
.
PauseNs
[(
m
.
NumGC
+
255
)
%
256
])
/
1000
/
1000
/
1000
)
sysStatus
.
NumGC
=
m
.
NumGC
}
func
adminResetPassword
(
app
*
App
,
u
*
User
,
newPass
string
)
error
{
hashedPass
,
err
:=
auth
.
HashPass
([]
byte
(
newPass
))
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not create password hash: %v"
,
err
)}
}
err
=
app
.
db
.
ChangePassphrase
(
u
.
ID
,
true
,
""
,
hashedPass
)
if
err
!=
nil
{
return
impart
.
HTTPError
{
http
.
StatusInternalServerError
,
fmt
.
Sprintf
(
"Could not update passphrase: %v"
,
err
)}
}
return
nil
}
func
handleViewAdminUpdates
(
app
*
App
,
u
*
User
,
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
error
{
check
:=
r
.
URL
.
Query
().
Get
(
"check"
)
if
check
==
"now"
&&
app
.
cfg
.
App
.
UpdateChecks
{
app
.
updates
.
CheckNow
()
}
p
:=
struct
{
*
UserPage
*
AdminPage
CurReleaseNotesURL
string
LastChecked
string
LastChecked8601
string
LatestVersion
string
LatestReleaseURL
string
LatestReleaseNotesURL
string
CheckFailed
bool
}{
UserPage
:
NewUserPage
(
app
,
r
,
u
,
"Updates"
,
nil
),
AdminPage
:
NewAdminPage
(
app
),
}
p
.
CurReleaseNotesURL
=
wfReleaseNotesURL
(
p
.
Version
)
if
app
.
cfg
.
App
.
UpdateChecks
{
p
.
LastChecked
=
app
.
updates
.
lastCheck
.
Format
(
"January 2, 2006, 3:04 PM"
)
p
.
LastChecked8601
=
app
.
updates
.
lastCheck
.
Format
(
"2006-01-02T15:04:05Z"
)
p
.
LatestVersion
=
app
.
updates
.
LatestVersion
()
p
.
LatestReleaseURL
=
app
.
updates
.
ReleaseURL
()
p
.
LatestReleaseNotesURL
=
app
.
updates
.
ReleaseNotesURL
()
p
.
UpdateAvailable
=
app
.
updates
.
AreAvailable
()
p
.
CheckFailed
=
app
.
updates
.
checkError
!=
nil
}
showUserPage
(
w
,
"app-updates"
,
p
)
return
nil
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 23, 5:29 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3104400
Attached To
rWF WriteFreely
Event Timeline
Log In to Comment