rewrite time!!
This commit is contained in:
parent
d80ee751fe
commit
a8de79ac50
11
LICENSE
11
LICENSE
|
@ -1,11 +0,0 @@
|
|||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
29
README.md
29
README.md
|
@ -1,29 +0,0 @@
|
|||
# admin
|
||||
quick little admin panel.
|
||||
|
||||
## todo
|
||||
- [ ] respectable web ui
|
||||
- [ ] signup form
|
||||
- [ ] lots of db code
|
||||
- [ ] gitea authentication
|
||||
- [ ] pam auth (maube?)
|
||||
- [ ] automatic user creaton/deletion
|
||||
- [ ] resource usage dashboard
|
||||
|
||||
## how 2 build
|
||||
|
||||
you need to build the stylesheet with tailwind first:
|
||||
```
|
||||
npm i -g tailwindcss
|
||||
npx tailwindcss -i ./resources/main.css -o ./resources/build/main.css --watch
|
||||
```
|
||||
|
||||
then just build it
|
||||
```
|
||||
go get
|
||||
go build
|
||||
```
|
||||
|
||||
### how 2 run
|
||||
|
||||
copy the config.example.yml file to config.yml and edit the values accordingly, then just run the binary and crack it open in your web browser.
|
|
@ -1,14 +0,0 @@
|
|||
server:
|
||||
name: "south london"
|
||||
homepage: "https://southlondon.cc"
|
||||
|
||||
admin:
|
||||
# webroot: "/admin" # Set this if you're running the admin panel on using a
|
||||
# custom path instead of a subdomain.
|
||||
# e.g. southlondon.cc/admin instead of admin.southlondon.cc
|
||||
|
||||
|
||||
auth:
|
||||
gitea-url: "https://tildegit.org"
|
||||
authorized-users:
|
||||
- lp0
|
|
@ -1,33 +0,0 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Username string
|
||||
Token string
|
||||
UserID string
|
||||
LastLogin time.Time
|
||||
}
|
||||
|
||||
var db *gorm.DB
|
||||
var err error
|
||||
|
||||
func Open(dbname string) {
|
||||
db, err = gorm.Open(sqlite.Open("data/"+dbname), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("couldn't connect to database")
|
||||
}
|
||||
}
|
||||
|
||||
func Migrate() {
|
||||
db.AutoMigrate(&User{})
|
||||
}
|
||||
|
||||
func (u *User) SaveUser() {
|
||||
|
||||
}
|
17
go.mod
17
go.mod
|
@ -1,17 +0,0 @@
|
|||
module tildegit.org/southlondon/admin
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gorm.io/gorm v1.22.5
|
||||
)
|
||||
|
||||
require github.com/mattn/go-sqlite3 v1.14.11 // indirect
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.4 // indirect
|
||||
gorm.io/driver/sqlite v1.2.6
|
||||
)
|
29
go.sum
29
go.sum
|
@ -1,29 +0,0 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
|
||||
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
|
||||
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU=
|
||||
gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
206
main.go
206
main.go
|
@ -1,206 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Config types
|
||||
// ------------
|
||||
type ServerConfig struct {
|
||||
Name string
|
||||
Homepage string
|
||||
}
|
||||
type PanelConfig struct {
|
||||
Host string
|
||||
WebRoot string
|
||||
Port int
|
||||
LogPath string
|
||||
}
|
||||
type AuthConfig struct {
|
||||
GiteaURL string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
AuthorizedUsers []string
|
||||
}
|
||||
type Config struct {
|
||||
Server ServerConfig
|
||||
Panel PanelConfig
|
||||
Auth AuthConfig
|
||||
}
|
||||
|
||||
var config = new(Config)
|
||||
var redirecturi string
|
||||
var requesturl string
|
||||
|
||||
func loadConfig() (err error) {
|
||||
configfile, err := ioutil.ReadFile("config.yml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(configfile, &config)
|
||||
|
||||
// todo: check for bad data !!
|
||||
|
||||
var re = regexp.MustCompile(`(((ftp|http|https):\/\/)|(\/)|(..\/))(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?`)
|
||||
tocheck := [2]string{config.Panel.Host, config.Auth.GiteaURL}
|
||||
|
||||
for _, val := range tocheck {
|
||||
if len(re.FindStringIndex(val)) == 0 {
|
||||
log.Fatalf(`"%s" is not a valid url.`, val)
|
||||
}
|
||||
}
|
||||
|
||||
if config.Panel.Port > 65535 {
|
||||
log.Fatalf("port is too big (max 65535)")
|
||||
}
|
||||
|
||||
// init oauth bits
|
||||
redirecturi = fmt.Sprintf("%s/login/endpoint", config.Panel.Host)
|
||||
requesturl = fmt.Sprintf("%s/login/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code&state=STATE",
|
||||
strings.Trim(config.Auth.GiteaURL, " "),
|
||||
strings.Trim(config.Auth.ClientID, " "),
|
||||
strings.Trim(redirecturi, " "))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func openLogFile(logfile string) {
|
||||
if logfile != "" {
|
||||
lf, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("OpenLogfile: os.OpenFile:", err)
|
||||
}
|
||||
|
||||
log.SetOutput(lf)
|
||||
}
|
||||
}
|
||||
|
||||
func logRequest(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := loadConfig()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("couldn't load config: %s", err)
|
||||
}
|
||||
|
||||
openLogFile(config.Panel.LogPath)
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
|
||||
http.HandleFunc("/login/", loginHandler)
|
||||
http.Handle("/resources/",
|
||||
http.StripPrefix("/resources/",
|
||||
http.FileServer(http.Dir("./resources"))))
|
||||
http.HandleFunc("/", handler)
|
||||
|
||||
fmt.Printf("listening on %v\n", config.Panel.Port)
|
||||
fmt.Printf("Logging to %v\n", config.Panel.LogPath)
|
||||
|
||||
err = http.ListenAndServe(fmt.Sprintf(":%d", config.Panel.Port), logRequest(http.DefaultServeMux))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func exists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.URL.Path
|
||||
loc := fmt.Sprintf("templates%s.html", path)
|
||||
|
||||
// var re = regexp.MustCompile(`\.(svg|jpg|jpeg|png|webp|ico|css|js)$`)
|
||||
|
||||
// if re.MatchString(path) {
|
||||
// http.StripPrefix("/resources/",
|
||||
// http.FileServer(http.Dir("./resources")))
|
||||
// return
|
||||
// }
|
||||
|
||||
var tmpl string
|
||||
|
||||
anonPath := regexp.MustCompile(`^/(signup|login|hello)$`)
|
||||
m := anonPath.FindStringSubmatch(path)
|
||||
|
||||
if m != nil {
|
||||
userid, err := r.Cookie("user-id")
|
||||
if errors.As(err, &http.ErrNoCookie) {
|
||||
http.Redirect(w, r, "/hello", http.StatusFound)
|
||||
}
|
||||
fmt.Print(userid)
|
||||
}
|
||||
|
||||
switch path {
|
||||
case "/":
|
||||
tmpl = "templates/home.html"
|
||||
t, _ := template.ParseFiles(tmpl)
|
||||
t.Execute(w, config)
|
||||
default:
|
||||
|
||||
exists, err := exists(loc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
tmpl = fmt.Sprintf("templates%s.html", path)
|
||||
t, _ := template.ParseFiles(tmpl)
|
||||
t.Execute(w, config)
|
||||
}
|
||||
}
|
||||
|
||||
func loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userid, err := r.Cookie("user-id")
|
||||
fmt.Print(userid, err)
|
||||
|
||||
if errors.As(err, &http.ErrNoCookie) {
|
||||
useridbody := uuid.New()
|
||||
expiration := time.Now().Add(365 * 24 * time.Hour)
|
||||
cookie := http.Cookie{Name: "user-id", Value: useridbody.String(), Expires: expiration}
|
||||
http.SetCookie(w, &cookie)
|
||||
// todo: store in db
|
||||
}
|
||||
|
||||
switch strings.TrimRight(r.URL.Path, "/") {
|
||||
case "/login":
|
||||
http.Redirect(w, r, requesturl, http.StatusFound)
|
||||
case "/login/endpoint":
|
||||
loginEndpoint(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func loginEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.FormValue("code")
|
||||
fmt.Print(token)
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
.login {
|
||||
@apply flex flex-row justify-center items-center m-auto shadow-md bg-slate-50 p-6 pb-8 rounded px-10 border-t-4 border-t-emerald-700
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.button {
|
||||
@apply w-full bg-gradient-to-t from-emerald-800 to-emerald-700 text-slate-100 rounded py-2 shadow-sm shadow-emerald-800 hover:opacity-95 font-bold;
|
||||
}
|
||||
input {
|
||||
@apply border-slate-300 border-2 rounded bg-slate-100 w-full my-1 p-1 px-2;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
module.exports = {
|
||||
content: ["./templates/*.html"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<title>{{.Server.Name}} Admin Panel</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
<link rel='stylesheet' type='text/css' media='screen' href='/resources/build/main.css'>
|
||||
</head>
|
||||
|
||||
<body class="bg-slate-200 flex min-h-screen">
|
||||
<div class="login">
|
||||
<form>
|
||||
<h3 class="font-bold mb-6 text-lg text-center">Log in to {{.Server.Name}} Admin</h3>
|
||||
<a href="/login" class="button my-10 px-16">
|
||||
Log In With Gitea
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,13 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<title>Page Title</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
||||
<link rel='stylesheet' type='text/css' media='screen' href='main.css'>
|
||||
</head>
|
||||
<body>
|
||||
wowzers
|
||||
</body>
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
leah you better write some fucking tests this time around
|
Reference in New Issue