Fastfix for previous two commits (sorry) and chroma update.
Well, doing "git add ." not from repository's root is a bad idea. Updated chroma dependency, added more languages to syntax highlighting.
This commit is contained in:
parent
3fe51fc6c5
commit
19b5ef3d9f
7
Gopkg.lock
generated
7
Gopkg.lock
generated
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:91a7ed02655beb94740c4cda2cf2208679537465515775be7ea6479e78cdd3ca"
|
digest = "1:aa94227e54ee105fcd03f25c85d3e5bbe13d2f2d5eb02379b5b4db5320dfb371"
|
||||||
name = "github.com/alecthomas/chroma"
|
name = "github.com/alecthomas/chroma"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
@ -12,6 +12,7 @@
|
|||||||
"lexers/a",
|
"lexers/a",
|
||||||
"lexers/b",
|
"lexers/b",
|
||||||
"lexers/c",
|
"lexers/c",
|
||||||
|
"lexers/circular",
|
||||||
"lexers/d",
|
"lexers/d",
|
||||||
"lexers/e",
|
"lexers/e",
|
||||||
"lexers/f",
|
"lexers/f",
|
||||||
@ -37,8 +38,8 @@
|
|||||||
"styles",
|
"styles",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "3020e2ea8c6b1a9c2336022d847c4392c3997f02"
|
revision = "881a441774f9d707d3b7852025b7f2149a556182"
|
||||||
version = "v0.4.0"
|
version = "v0.6.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
6
assets/database_not_available.html
Normal file
6
assets/database_not_available.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<section class="section">
|
||||||
|
<div class="notification is-danger">
|
||||||
|
<h3><strong>Database not available</strong></h3>
|
||||||
|
<p>Something went wrong while trying to connect to database. Check logs for details.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
183
assets/static/ab0x.go
Normal file
183
assets/static/ab0x.go
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// Code generated by fileb0x at "2019-03-07 08:10:26.564250656 +0500 +05 m=+0.012981739" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modification hash(c461fa235611d8291f5f88ea81ba2ff4.c75689541d811d1d99d6d26d3d38a28f)
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
|
||||||
|
"golang.org/x/net/webdav"
|
||||||
|
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// CTX is a context for webdav vfs
|
||||||
|
CTX = context.Background()
|
||||||
|
|
||||||
|
|
||||||
|
// FS is a virtual memory file system
|
||||||
|
FS = webdav.NewMemFS()
|
||||||
|
|
||||||
|
|
||||||
|
// Handler is used to server files through a http handler
|
||||||
|
Handler *webdav.Handler
|
||||||
|
|
||||||
|
// HTTP is the http file system
|
||||||
|
HTTP http.FileSystem = new(HTTPFS)
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPFS implements http.FileSystem
|
||||||
|
type HTTPFS struct {
|
||||||
|
// Prefix allows to limit the path of all requests. F.e. a prefix "css" would allow only calls to /css/*
|
||||||
|
Prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
err := CTX.Err()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
err = FS.Mkdir(CTX, "static/", 0777)
|
||||||
|
if err != nil && err != os.ErrExist {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
err = FS.Mkdir(CTX, "static/css/", 0777)
|
||||||
|
if err != nil && err != os.ErrExist {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
err = FS.Mkdir(CTX, "static/js/", 0777)
|
||||||
|
if err != nil && err != os.ErrExist {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Handler = &webdav.Handler{
|
||||||
|
FileSystem: FS,
|
||||||
|
LockSystem: webdav.NewMemLS(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Open a file
|
||||||
|
func (hfs *HTTPFS) Open(path string) (http.File, error) {
|
||||||
|
path = hfs.Prefix + path
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, path, os.O_RDONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFile is adapTed from ioutil
|
||||||
|
func ReadFile(path string) ([]byte, error) {
|
||||||
|
f, err := FS.OpenFile(CTX, path, os.O_RDONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
|
||||||
|
|
||||||
|
// If the buffer overflows, we will get bytes.ErrTooLarge.
|
||||||
|
// Return that as an error. Any other panic remains.
|
||||||
|
defer func() {
|
||||||
|
e := recover()
|
||||||
|
if e == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
|
||||||
|
err = panicErr
|
||||||
|
} else {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
_, err = buf.ReadFrom(f)
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile is adapTed from ioutil
|
||||||
|
func WriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||||
|
f, err := FS.OpenFile(CTX, filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n, err := f.Write(data)
|
||||||
|
if err == nil && n < len(data) {
|
||||||
|
err = io.ErrShortWrite
|
||||||
|
}
|
||||||
|
if err1 := f.Close(); err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkDirs looks for files in the given dir and returns a list of files in it
|
||||||
|
// usage for all files in the b0x: WalkDirs("", false)
|
||||||
|
func WalkDirs(name string, includeDirsInList bool, files ...string) ([]string, error) {
|
||||||
|
f, err := FS.OpenFile(CTX, name, os.O_RDONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInfos, err := f.Readdir(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range fileInfos {
|
||||||
|
filename := path.Join(name, info.Name())
|
||||||
|
|
||||||
|
if includeDirsInList || !info.IsDir() {
|
||||||
|
files = append(files, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
files, err = WalkDirs(filename, includeDirsInList, files...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
35
assets/static/b0xfile__database_not_available.html.go
Normal file
35
assets/static/b0xfile__database_not_available.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-07 08:10:26.566124087 +0500 +05 m=+0.014855200" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2019-03-07 08:09:33.842483985 +0500 +05)
|
||||||
|
// original path: assets/database_not_available.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileDatabaseNotAvailableHTML is "/database_not_available.html"
|
||||||
|
var FileDatabaseNotAvailableHTML = []byte("\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x6f\x74\x69\x66\x69\x63\x61\x74\x69\x6f\x6e\x20\x69\x73\x2d\x64\x61\x6e\x67\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x68\x33\x3e\x3c\x73\x74\x72\x6f\x6e\x67\x3e\x44\x61\x74\x61\x62\x61\x73\x65\x20\x6e\x6f\x74\x20\x61\x76\x61\x69\x6c\x61\x62\x6c\x65\x3c\x2f\x73\x74\x72\x6f\x6e\x67\x3e\x3c\x2f\x68\x33\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x3e\x53\x6f\x6d\x65\x74\x68\x69\x6e\x67\x20\x77\x65\x6e\x74\x20\x77\x72\x6f\x6e\x67\x20\x77\x68\x69\x6c\x65\x20\x74\x72\x79\x69\x6e\x67\x20\x74\x6f\x20\x63\x6f\x6e\x6e\x65\x63\x74\x20\x74\x6f\x20\x64\x61\x74\x61\x62\x61\x73\x65\x2e\x20\x43\x68\x65\x63\x6b\x20\x6c\x6f\x67\x73\x20\x66\x6f\x72\x20\x64\x65\x74\x61\x69\x6c\x73\x2e\x3c\x2f\x70\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/database_not_available.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FileDatabaseNotAvailableHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__error.html.go
Normal file
35
assets/static/b0xfile__error.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.48097074 +0500 +05 m=+0.028043901" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-26 13:29:04 +0500 +05)
|
||||||
|
// original path: assets/error.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileErrorHTML is "/error.html"
|
||||||
|
var FileErrorHTML = []byte("\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x62\x6f\x78\x20\x68\x61\x73\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x77\x61\x72\x6e\x69\x6e\x67\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x3e\x7b\x65\x72\x72\x6f\x72\x7d\x3c\x2f\x70\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/error.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FileErrorHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__footer.html.go
Normal file
35
assets/static/b0xfile__footer.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.483691702 +0500 +05 m=+0.030764857" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-12-01 04:46:07 +0500 +05)
|
||||||
|
// original path: assets/footer.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileFooterHTML is "/footer.html"
|
||||||
|
var FileFooterHTML = []byte("\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6f\x6e\x74\x61\x69\x6e\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6f\x6e\x74\x65\x6e\x74\x20\x68\x61\x73\x2d\x74\x65\x78\x74\x2d\x63\x65\x6e\x74\x65\x72\x65\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x72\x6f\x6e\x67\x3e\x46\x61\x73\x74\x20\x70\x61\x73\x74\x65\x20\x62\x69\x6e\x3c\x2f\x73\x74\x72\x6f\x6e\x67\x3e\x20\x76\x65\x72\x73\x69\x6f\x6e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x72\x6f\x6e\x67\x3e\x7b\x76\x65\x72\x73\x69\x6f\x6e\x7d\x3c\x2f\x73\x74\x72\x6f\x6e\x67\x3e\x20\x62\x79\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x68\x74\x74\x70\x73\x3a\x2f\x2f\x70\x7a\x74\x72\x6e\x2e\x6e\x61\x6d\x65\x22\x3e\x53\x74\x61\x6e\x69\x73\x6c\x61\x76\x20\x4e\x2e\x20\x61\x6b\x61\x20\x70\x7a\x74\x72\x6e\x3c\x2f\x61\x3e\x2e\x20\x54\x68\x65\x20\x73\x6f\x75\x72\x63\x65\x20\x63\x6f\x64\x65\x20\x69\x73\x20\x6c\x69\x63\x65\x6e\x73\x65\x64\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6f\x70\x65\x6e\x73\x6f\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x6c\x69\x63\x65\x6e\x73\x65\x73\x2f\x6d\x69\x74\x2d\x6c\x69\x63\x65\x6e\x73\x65\x2e\x70\x68\x70\x22\x3e\x4d\x49\x54\x3c\x2f\x61\x3e\x2e\x20\x47\x65\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x68\x74\x74\x70\x73\x3a\x2f\x2f\x67\x69\x74\x6c\x61\x62\x2e\x63\x6f\x6d\x2f\x70\x7a\x74\x72\x6e\x2f\x66\x61\x73\x74\x70\x61\x73\x74\x65\x62\x69\x6e\x22\x3e\x73\x6f\x75\x72\x63\x65\x20\x6f\x72\x20\x62\x69\x6e\x61\x72\x79\x20\x72\x65\x6c\x65\x61\x73\x65\x73\x20\x68\x65\x72\x65\x3c\x2f\x61\x3e\x21\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x70\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x64\x69\x76\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/footer.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FileFooterHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__index.html.go
Normal file
35
assets/static/b0xfile__index.html.go
Normal file
File diff suppressed because one or more lines are too long
35
assets/static/b0xfile__main.html.go
Normal file
35
assets/static/b0xfile__main.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.482994435 +0500 +05 m=+0.030067594" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-26 12:41:36 +0500 +05)
|
||||||
|
// original path: assets/main.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileMainHTML is "/main.html"
|
||||||
|
var FileMainHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x74\x6f\x6f\x6c\x74\x69\x70\x2d\x31\x2e\x30\x2e\x34\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x7b\x6e\x61\x76\x69\x67\x61\x74\x69\x6f\x6e\x7d\x20\x7b\x64\x6f\x63\x75\x6d\x65\x6e\x74\x42\x6f\x64\x79\x7d\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x3c\x66\x6f\x6f\x74\x65\x72\x20\x63\x6c\x61\x73\x73\x3d\x22\x66\x6f\x6f\x74\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x7b\x66\x6f\x6f\x74\x65\x72\x7d\x0a\x3c\x2f\x66\x6f\x6f\x74\x65\x72\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/main.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FileMainHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__navigation.html.go
Normal file
35
assets/static/b0xfile__navigation.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.484008051 +0500 +05 m=+0.031081226" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-26 13:57:14 +0500 +05)
|
||||||
|
// original path: assets/navigation.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileNavigationHTML is "/navigation.html"
|
||||||
|
var FileNavigationHTML = []byte("\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x6e\x61\x76\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/navigation.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FileNavigationHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__pagination.html.go
Normal file
35
assets/static/b0xfile__pagination.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.48066993 +0500 +05 m=+0.027743088" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-01 00:32:51 +0500 +05)
|
||||||
|
// original path: assets/pagination.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePaginationHTML is "/pagination.html"
|
||||||
|
var FilePaginationHTML = []byte("\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x20\x69\x73\x2d\x63\x65\x6e\x74\x65\x72\x65\x64\x22\x20\x72\x6f\x6c\x65\x3d\x22\x6e\x61\x76\x69\x67\x61\x74\x69\x6f\x6e\x22\x20\x61\x72\x69\x61\x2d\x6c\x61\x62\x65\x6c\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x2d\x70\x72\x65\x76\x69\x6f\x75\x73\x22\x20\x68\x72\x65\x66\x3d\x22\x7b\x70\x72\x65\x76\x69\x6f\x75\x73\x50\x61\x67\x65\x4c\x69\x6e\x6b\x7d\x22\x3e\x50\x72\x65\x76\x69\x6f\x75\x73\x20\x70\x61\x67\x65\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x75\x6c\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x2d\x6c\x69\x73\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x4c\x69\x6e\x6b\x73\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x75\x6c\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x2d\x6e\x65\x78\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x7b\x6e\x65\x78\x74\x50\x61\x67\x65\x4c\x69\x6e\x6b\x7d\x22\x3e\x4e\x65\x78\x74\x20\x70\x61\x67\x65\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6e\x61\x76\x3e\x0a\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/pagination.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FilePaginationHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__pagination_ellipsis.html.go
Normal file
35
assets/static/b0xfile__pagination_ellipsis.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.481183931 +0500 +05 m=+0.028257091" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-04-30 23:39:44 +0500 +05)
|
||||||
|
// original path: assets/pagination_ellipsis.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePaginationEllipsisHTML is "/pagination_ellipsis.html"
|
||||||
|
var FilePaginationEllipsisHTML = []byte("\x3c\x6c\x69\x3e\x0a\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x2d\x65\x6c\x6c\x69\x70\x73\x69\x73\x22\x3e\x26\x68\x65\x6c\x6c\x69\x70\x3b\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x3c\x2f\x6c\x69\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/pagination_ellipsis.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FilePaginationEllipsisHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__pagination_link.html.go
Normal file
35
assets/static/b0xfile__pagination_link.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.484260698 +0500 +05 m=+0.031333867" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-04-30 23:54:11 +0500 +05)
|
||||||
|
// original path: assets/pagination_link.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePaginationLinkHTML is "/pagination_link.html"
|
||||||
|
var FilePaginationLinkHTML = []byte("\x3c\x6c\x69\x3e\x0a\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x2d\x6c\x69\x6e\x6b\x22\x20\x61\x72\x69\x61\x2d\x6c\x61\x62\x65\x6c\x3d\x22\x47\x6f\x20\x74\x6f\x20\x70\x61\x67\x65\x20\x7b\x70\x61\x67\x65\x4e\x75\x6d\x7d\x22\x20\x68\x72\x65\x66\x3d\x22\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x4c\x69\x6e\x6b\x7d\x22\x3e\x7b\x70\x61\x67\x65\x4e\x75\x6d\x7d\x3c\x2f\x61\x3e\x0a\x3c\x2f\x6c\x69\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/pagination_link.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FilePaginationLinkHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__pagination_link_current.html.go
Normal file
35
assets/static/b0xfile__pagination_link_current.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.481387829 +0500 +05 m=+0.028460988" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-04-30 23:54:14 +0500 +05)
|
||||||
|
// original path: assets/pagination_link_current.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePaginationLinkCurrentHTML is "/pagination_link_current.html"
|
||||||
|
var FilePaginationLinkCurrentHTML = []byte("\x3c\x6c\x69\x3e\x0a\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x2d\x6c\x69\x6e\x6b\x20\x69\x73\x2d\x63\x75\x72\x72\x65\x6e\x74\x22\x20\x61\x72\x69\x61\x2d\x6c\x61\x62\x65\x6c\x3d\x22\x50\x61\x67\x65\x20\x7b\x70\x61\x67\x65\x4e\x75\x6d\x7d\x22\x20\x61\x72\x69\x61\x2d\x63\x75\x72\x72\x65\x6e\x74\x3d\x22\x70\x61\x67\x65\x22\x3e\x7b\x70\x61\x67\x65\x4e\x75\x6d\x7d\x3c\x2f\x61\x3e\x0a\x3c\x2f\x6c\x69\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/pagination_link_current.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FilePaginationLinkCurrentHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__passworded_paste_verify.html.go
Normal file
35
assets/static/b0xfile__passworded_paste_verify.html.go
Normal file
File diff suppressed because one or more lines are too long
35
assets/static/b0xfile__paste.html.go
Normal file
35
assets/static/b0xfile__paste.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.48161185 +0500 +05 m=+0.028685005" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-26 13:24:18 +0500 +05)
|
||||||
|
// original path: assets/paste.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePasteHTML is "/paste.html"
|
||||||
|
var FilePasteHTML = []byte("\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6f\x6e\x74\x65\x6e\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x61\x62\x6c\x65\x20\x63\x6c\x61\x73\x73\x3d\x22\x74\x61\x62\x6c\x65\x20\x69\x73\x2d\x62\x6f\x72\x64\x65\x72\x65\x64\x20\x69\x73\x2d\x73\x74\x72\x69\x70\x65\x64\x20\x69\x73\x2d\x6e\x61\x72\x72\x6f\x77\x20\x69\x73\x2d\x68\x6f\x76\x65\x72\x61\x62\x6c\x65\x20\x69\x73\x2d\x66\x75\x6c\x6c\x77\x69\x64\x74\x68\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x72\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x68\x3e\x23\x3c\x2f\x74\x68\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x68\x3e\x54\x69\x74\x6c\x65\x3c\x2f\x74\x68\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x68\x3e\x4c\x61\x6e\x67\x75\x61\x67\x65\x3c\x2f\x74\x68\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x68\x3e\x50\x61\x73\x74\x65\x64\x20\x6f\x6e\x3c\x2f\x74\x68\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x68\x3e\x57\x69\x6c\x6c\x20\x65\x78\x70\x69\x72\x65\x20\x6f\x6e\x3c\x2f\x74\x68\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x68\x3e\x50\x61\x73\x74\x65\x20\x74\x79\x70\x65\x3c\x2f\x74\x68\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x74\x72\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x74\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x72\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x64\x3e\x7b\x70\x61\x73\x74\x65\x49\x44\x7d\x3c\x2f\x74\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x64\x3e\x7b\x70\x61\x73\x74\x65\x54\x69\x74\x6c\x65\x7d\x3c\x2f\x74\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x64\x3e\x7b\x70\x61\x73\x74\x65\x4c\x61\x6e\x67\x75\x61\x67\x65\x7d\x3c\x2f\x74\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x64\x3e\x7b\x70\x61\x73\x74\x65\x44\x61\x74\x65\x7d\x3c\x2f\x74\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x64\x3e\x7b\x70\x61\x73\x74\x65\x45\x78\x70\x69\x72\x61\x74\x69\x6f\x6e\x7d\x3c\x2f\x74\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x64\x3e\x7b\x70\x61\x73\x74\x65\x54\x79\x70\x65\x7d\x3c\x2f\x74\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x74\x72\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x72\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x74\x64\x20\x63\x6f\x6c\x73\x70\x61\x6e\x3d\x22\x36\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x62\x75\x74\x74\x6f\x6e\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x2f\x7b\x70\x61\x73\x74\x65\x49\x44\x7d\x2f\x7b\x70\x61\x73\x74\x65\x54\x73\x7d\x72\x61\x77\x22\x3e\x56\x69\x65\x77\x20\x72\x61\x77\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x74\x64\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x74\x72\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x74\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x74\x61\x62\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e\x0a\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x70\x61\x73\x74\x65\x2d\x64\x61\x74\x61\x22\x3e\x0a\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x64\x61\x74\x61\x7d\x0a\x3c\x2f\x64\x69\x76\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/paste.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FilePasteHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__pastelist_list.html.go
Normal file
35
assets/static/b0xfile__pastelist_list.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.484508366 +0500 +05 m=+0.031581521" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-26 13:41:01 +0500 +05)
|
||||||
|
// original path: assets/pastelist_list.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePastelistListHTML is "/pastelist_list.html"
|
||||||
|
var FilePastelistListHTML = []byte("\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x73\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/pastelist_list.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FilePastelistListHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile__pastelist_paste.html.go
Normal file
35
assets/static/b0xfile__pastelist_paste.html.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.481867292 +0500 +05 m=+0.028940448" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-01 18:35:21 +0500 +05)
|
||||||
|
// original path: assets/pastelist_paste.html
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePastelistPasteHTML is "/pastelist_paste.html"
|
||||||
|
var FilePastelistPasteHTML = []byte("\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6f\x6e\x74\x65\x6e\x74\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x61\x72\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x68\x65\x61\x64\x65\x72\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x61\x72\x64\x2d\x68\x65\x61\x64\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x61\x72\x64\x2d\x68\x65\x61\x64\x65\x72\x2d\x74\x69\x74\x6c\x65\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x20\x23\x7b\x70\x61\x73\x74\x65\x49\x44\x7d\x2c\x20\x70\x6f\x73\x74\x65\x64\x20\x6f\x6e\x20\x7b\x70\x61\x73\x74\x65\x44\x61\x74\x65\x7d\x20\x61\x6e\x64\x20\x74\x69\x74\x6c\x65\x64\x20\x61\x73\x20\x22\x7b\x70\x61\x73\x74\x65\x54\x69\x74\x6c\x65\x7d\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x70\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x68\x65\x61\x64\x65\x72\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x61\x72\x64\x2d\x63\x6f\x6e\x74\x65\x6e\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6f\x6e\x74\x65\x6e\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x72\x65\x3e\x7b\x70\x61\x73\x74\x65\x44\x61\x74\x61\x7d\x3c\x2f\x70\x72\x65\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x66\x6f\x6f\x74\x65\x72\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x61\x72\x64\x2d\x66\x6f\x6f\x74\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x2f\x7b\x70\x61\x73\x74\x65\x49\x44\x7d\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x61\x72\x64\x2d\x66\x6f\x6f\x74\x65\x72\x2d\x69\x74\x65\x6d\x20\x62\x75\x74\x74\x6f\x6e\x20\x69\x73\x2d\x73\x75\x63\x63\x65\x73\x73\x20\x69\x73\x2d\x72\x61\x64\x69\x75\x73\x6c\x65\x73\x73\x22\x3e\x56\x69\x65\x77\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x66\x6f\x6f\x74\x65\x72\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x64\x69\x76\x3e")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "/pastelist_paste.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FilePastelistPasteHTML)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile_static_css_bulma-0.7.0.min.css.go
Normal file
35
assets/static/b0xfile_static_css_bulma-0.7.0.min.css.go
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
35
assets/static/b0xfile_static_css_bulma.css.map.go
Normal file
35
assets/static/b0xfile_static_css_bulma.css.map.go
Normal file
File diff suppressed because one or more lines are too long
35
assets/static/b0xfile_static_css_style.css.go
Normal file
35
assets/static/b0xfile_static_css_style.css.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generaTed by fileb0x at "2019-03-06 15:05:53.472324169 +0500 +05 m=+0.019397329" from config file "fileb0x.yml" DO NOT EDIT.
|
||||||
|
// modified(2018-05-01 17:43:40 +0500 +05)
|
||||||
|
// original path: assets/css/style.css
|
||||||
|
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileStaticCSSStyleCSS is "static/css/style.css"
|
||||||
|
var FileStaticCSSStyleCSS = []byte("\x23\x70\x61\x73\x74\x65\x2d\x63\x6f\x6e\x74\x65\x6e\x74\x73\x20\x7b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x66\x61\x6d\x69\x6c\x79\x3a\x20\x6d\x6f\x6e\x6f\x73\x70\x61\x63\x65\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\x65\x3a\x20\x30\x2e\x39\x72\x65\x6d\x3b\x0a\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3a\x20\x39\x30\x76\x68\x3b\x0a\x7d\x0a\x0a\x2e\x70\x61\x73\x74\x65\x2d\x64\x61\x74\x61\x20\x7b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\x65\x3a\x20\x30\x2e\x39\x72\x65\x6d\x3b\x0a\x7d")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
|
||||||
|
f, err := FS.OpenFile(CTX, "static/css/style.css", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_, err = f.Write(FileStaticCSSStyleCSS)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
35
assets/static/b0xfile_static_js_fontawesome-5.0.7.js.go
Normal file
35
assets/static/b0xfile_static_js_fontawesome-5.0.7.js.go
Normal file
File diff suppressed because one or more lines are too long
47
domains/database_not_available/dbnotavailable.go
Normal file
47
domains/database_not_available/dbnotavailable.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package database_not_available
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/templater"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Database not available error page
|
||||||
|
func dbNotAvailableGet(ec echo.Context) error {
|
||||||
|
htmlData := templater.GetTemplate(ec, "database_not_available.html", nil)
|
||||||
|
|
||||||
|
return ec.HTML(http.StatusInternalServerError, htmlData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbNotAvailableRawGet(ec echo.Context) error {
|
||||||
|
return ec.String(http.StatusInternalServerError, "Database not available\nSomething went wrong while trying to connect to database. Check logs for details.")
|
||||||
|
}
|
43
domains/database_not_available/exported.go
Normal file
43
domains/database_not_available/exported.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package database_not_available
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes pastes package and adds neccessary HTTP and API
|
||||||
|
// endpoints.
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
|
||||||
|
c.Echo.GET("/database_not_available", dbNotAvailableGet)
|
||||||
|
c.Echo.GET("/database_not_available/raw", dbNotAvailableRawGet)
|
||||||
|
}
|
42
domains/indexpage/exported.go
Normal file
42
domains/indexpage/exported.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package indexpage
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes pastes package and adds neccessary HTTP and API
|
||||||
|
// endpoints.
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
|
||||||
|
c.Echo.GET("/", indexGet)
|
||||||
|
}
|
62
domains/indexpage/indexpage.go
Normal file
62
domains/indexpage/indexpage.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package indexpage
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/captcha"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/templater"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/alecthomas/chroma/lexers"
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Index of this site.
|
||||||
|
func indexGet(ec echo.Context) error {
|
||||||
|
// We should check if database connection available.
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if c.Config.Database.Type != "flatfiles" && dbConn == nil {
|
||||||
|
ec.Redirect(http.StatusFound, "/database_not_available")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate list of available languages to highlight.
|
||||||
|
availableLexers := lexers.Names(false)
|
||||||
|
|
||||||
|
var availableLexersSelectOpts = "<option value='text'>Text</option><option value='autodetect'>Auto detect</option><option disabled>-----</option>"
|
||||||
|
for i := range availableLexers {
|
||||||
|
availableLexersSelectOpts += "<option value='" + availableLexers[i] + "'>" + availableLexers[i] + "</option>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captcha.
|
||||||
|
captchaString := captcha.NewCaptcha()
|
||||||
|
|
||||||
|
htmlData := templater.GetTemplate(ec, "index.html", map[string]string{"lexers": availableLexersSelectOpts, "captchaString": captchaString})
|
||||||
|
|
||||||
|
return ec.HTML(http.StatusOK, htmlData)
|
||||||
|
}
|
499
domains/pastes/api_http.go
Normal file
499
domains/pastes/api_http.go
Normal file
@ -0,0 +1,499 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package pastes
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/captcha"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/pagination"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/templater"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/alecthomas/chroma"
|
||||||
|
"github.com/alecthomas/chroma/formatters"
|
||||||
|
htmlfmt "github.com/alecthomas/chroma/formatters/html"
|
||||||
|
"github.com/alecthomas/chroma/lexers"
|
||||||
|
"github.com/alecthomas/chroma/styles"
|
||||||
|
//"gitlab.com/dchest/captcha"
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
regexInts = regexp.MustCompile("[0-9]+")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GET for "/paste/PASTE_ID" and "/paste/PASTE_ID/TIMESTAMP" (private pastes).
|
||||||
|
func pasteGET(ec echo.Context) error {
|
||||||
|
// We should check if database connection available.
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if c.Config.Database.Type != "flatfiles" && dbConn == nil {
|
||||||
|
return ec.Redirect(http.StatusFound, "/database_not_available")
|
||||||
|
}
|
||||||
|
|
||||||
|
pasteIDRaw := ec.Param("id")
|
||||||
|
// We already get numbers from string, so we will not check strconv.Atoi()
|
||||||
|
// error.
|
||||||
|
pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
|
||||||
|
c.Logger.Debug().Msgf("Requesting paste #%+v", pasteID)
|
||||||
|
|
||||||
|
// Get paste.
|
||||||
|
paste, err1 := c.Database.GetPaste(pasteID)
|
||||||
|
if err1 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if paste.IsExpired() {
|
||||||
|
c.Logger.Error().Msgf("Paste #%d is expired", pasteID)
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have a private paste and it's parameters are correct.
|
||||||
|
if paste.Private {
|
||||||
|
tsProvidedStr := ec.Param("timestamp")
|
||||||
|
tsProvided, err2 := strconv.ParseInt(tsProvidedStr, 10, 64)
|
||||||
|
if err2 != nil {
|
||||||
|
c.Logger.Error().Msgf("Invalid timestamp '%s' provided for getting private paste #%d: %s", tsProvidedStr, pasteID, err2.Error())
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
pasteTs := paste.CreatedAt.Unix()
|
||||||
|
if tsProvided != pasteTs {
|
||||||
|
c.Logger.Error().Msgf("Incorrect timestamp '%v' provided for private paste #%d, waiting for %v", tsProvidedStr, pasteID, strconv.FormatInt(pasteTs, 10))
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if paste.Private && paste.Password != "" {
|
||||||
|
// Check if cookie for this paste is defined. This means that user
|
||||||
|
// previously successfully entered a password.
|
||||||
|
cookie, err := ec.Cookie("PASTE-" + strconv.Itoa(pasteID))
|
||||||
|
if err != nil {
|
||||||
|
// No cookie, redirect to auth page.
|
||||||
|
c.Logger.Info().Msg("Tried to access passworded paste without autorization, redirecting to auth page...")
|
||||||
|
return ec.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDRaw+"/"+ec.Param("timestamp")+"/verify")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate cookie value to check.
|
||||||
|
cookieValue := paste.GenerateCryptedCookieValue()
|
||||||
|
|
||||||
|
if cookieValue != cookie.Value {
|
||||||
|
c.Logger.Info().Msg("Invalid cookie, redirecting to auth page...")
|
||||||
|
return ec.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDRaw+"/"+ec.Param("timestamp")+"/verify")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all okay - do nothing :)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format paste data map.
|
||||||
|
pasteData := make(map[string]string)
|
||||||
|
pasteData["pasteTitle"] = paste.Title
|
||||||
|
pasteData["pasteID"] = strconv.Itoa(paste.ID)
|
||||||
|
pasteData["pasteDate"] = paste.CreatedAt.Format("2006-01-02 @ 15:04:05") + " UTC"
|
||||||
|
pasteData["pasteLanguage"] = paste.Language
|
||||||
|
|
||||||
|
pasteExpirationString := "Never"
|
||||||
|
if paste.KeepFor != 0 && paste.KeepForUnitType != 0 {
|
||||||
|
pasteExpirationString = paste.GetExpirationTime().Format("2006-01-02 @ 15:04:05") + " UTC"
|
||||||
|
}
|
||||||
|
pasteData["pasteExpiration"] = pasteExpirationString
|
||||||
|
|
||||||
|
if paste.Private {
|
||||||
|
pasteData["pasteType"] = "<span class='has-text-danger'>Private</span>"
|
||||||
|
pasteData["pasteTs"] = strconv.FormatInt(paste.CreatedAt.Unix(), 10) + "/"
|
||||||
|
} else {
|
||||||
|
pasteData["pasteType"] = "<span class='has-text-success'>Public</span>"
|
||||||
|
pasteData["pasteTs"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight.
|
||||||
|
// Get lexer.
|
||||||
|
lexer := lexers.Get(paste.Language)
|
||||||
|
if lexer == nil {
|
||||||
|
lexer = lexers.Fallback
|
||||||
|
}
|
||||||
|
// Tokenize paste data.
|
||||||
|
lexered, err3 := lexer.Tokenise(nil, paste.Data)
|
||||||
|
if err3 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to tokenize paste data: %s", err3.Error())
|
||||||
|
}
|
||||||
|
// Get style for HTML output.
|
||||||
|
style := styles.Get("monokai")
|
||||||
|
if style == nil {
|
||||||
|
style = styles.Fallback
|
||||||
|
}
|
||||||
|
// Get HTML formatter.
|
||||||
|
formatter := chroma.Formatter(htmlfmt.New(htmlfmt.WithLineNumbers(), htmlfmt.LineNumbersInTable()))
|
||||||
|
if formatter == nil {
|
||||||
|
formatter = formatters.Fallback
|
||||||
|
}
|
||||||
|
// Create buffer and format into it.
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err4 := formatter.Format(buf, style, lexered)
|
||||||
|
if err4 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to format paste data: %s", err4.Error())
|
||||||
|
}
|
||||||
|
pasteData["pastedata"] = buf.String()
|
||||||
|
|
||||||
|
// Get template and format it.
|
||||||
|
pasteHTML := templater.GetTemplate(ec, "paste.html", pasteData)
|
||||||
|
|
||||||
|
return ec.HTML(http.StatusOK, pasteHTML)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET for "/paste/PASTE_ID/TIMESTAMP/verify" - a password verify page.
|
||||||
|
func pastePasswordedVerifyGet(ec echo.Context) error {
|
||||||
|
// We should check if database connection available.
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if c.Config.Database.Type != "flatfiles" && dbConn == nil {
|
||||||
|
return ec.Redirect(http.StatusFound, "/database_not_available")
|
||||||
|
}
|
||||||
|
|
||||||
|
pasteIDRaw := ec.Param("id")
|
||||||
|
timestampRaw := ec.Param("timestamp")
|
||||||
|
// We already get numbers from string, so we will not check strconv.Atoi()
|
||||||
|
// error.
|
||||||
|
pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
|
||||||
|
|
||||||
|
// Get paste.
|
||||||
|
paste, err1 := c.Database.GetPaste(pasteID)
|
||||||
|
if err1 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Paste #"+pasteIDRaw+" not found")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for auth cookie. If present - redirect to paste.
|
||||||
|
cookie, err := ec.Cookie("PASTE-" + strconv.Itoa(pasteID))
|
||||||
|
if err == nil {
|
||||||
|
// No cookie, redirect to auth page.
|
||||||
|
c.Logger.Debug().Msg("Paste cookie found, checking it...")
|
||||||
|
|
||||||
|
// Generate cookie value to check.
|
||||||
|
cookieValue := paste.GenerateCryptedCookieValue()
|
||||||
|
|
||||||
|
if cookieValue == cookie.Value {
|
||||||
|
c.Logger.Info().Msg("Valid cookie, redirecting to paste page...")
|
||||||
|
return ec.Redirect(http.StatusMovedPermanently, "/paste/"+pasteIDRaw+"/"+ec.Param("timestamp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Debug().Msg("Invalid cookie, showing auth page")
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML data.
|
||||||
|
htmlData := make(map[string]string)
|
||||||
|
htmlData["pasteID"] = strconv.Itoa(pasteID)
|
||||||
|
htmlData["pasteTimestamp"] = timestampRaw
|
||||||
|
|
||||||
|
verifyHTML := templater.GetTemplate(ec, "passworded_paste_verify.html", htmlData)
|
||||||
|
|
||||||
|
return ec.HTML(http.StatusOK, verifyHTML)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST for "/paste/PASTE_ID/TIMESTAMP/verify" - a password verify page.
|
||||||
|
func pastePasswordedVerifyPost(ec echo.Context) error {
|
||||||
|
// We should check if database connection available.
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if c.Config.Database.Type != "flatfiles" && dbConn == nil {
|
||||||
|
return ec.Redirect(http.StatusFound, "/database_not_available")
|
||||||
|
}
|
||||||
|
|
||||||
|
pasteIDRaw := ec.Param("id")
|
||||||
|
timestampRaw := ec.Param("timestamp")
|
||||||
|
// We already get numbers from string, so we will not check strconv.Atoi()
|
||||||
|
// error.
|
||||||
|
pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
|
||||||
|
c.Logger.Debug().Msgf("Requesting paste #%+v", pasteID)
|
||||||
|
|
||||||
|
// Get paste.
|
||||||
|
paste, err1 := c.Database.GetPaste(pasteID)
|
||||||
|
if err1 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err2 := ec.FormParams()
|
||||||
|
if err2 != nil {
|
||||||
|
c.Logger.Debug().Msg("No form parameters passed")
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if paste.VerifyPassword(params["paste-password"][0]) {
|
||||||
|
// Set cookie that this paste's password is verified and paste
|
||||||
|
// can be viewed.
|
||||||
|
cookie := new(http.Cookie)
|
||||||
|
cookie.Name = "PASTE-" + strconv.Itoa(pasteID)
|
||||||
|
cookie.Value = paste.GenerateCryptedCookieValue()
|
||||||
|
cookie.Expires = time.Now().Add(24 * time.Hour)
|
||||||
|
ec.SetCookie(cookie)
|
||||||
|
|
||||||
|
return ec.Redirect(http.StatusFound, "/paste/"+strconv.Itoa(pasteID)+"/"+timestampRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Invalid password. Please, try again.")
|
||||||
|
return ec.HTML(http.StatusBadRequest, string(errtpl))
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST for "/paste/" which will create new paste and redirect to
|
||||||
|
// "/pastes/CREATED_PASTE_ID".
|
||||||
|
func pastePOST(ec echo.Context) error {
|
||||||
|
// We should check if database connection available.
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if c.Config.Database.Type != "flatfiles" && dbConn == nil {
|
||||||
|
return ec.Redirect(http.StatusFound, "/database_not_available")
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := ec.FormParams()
|
||||||
|
if err != nil {
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Cannot create empty paste")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
c.Logger.Debug().Msgf("Received parameters: %+v", params)
|
||||||
|
|
||||||
|
// Do nothing if paste contents is empty.
|
||||||
|
if len(params["paste-contents"][0]) == 0 {
|
||||||
|
c.Logger.Debug().Msg("Empty paste submitted, ignoring")
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Empty pastes aren't allowed.")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.ContainsAny(params["paste-keep-for"][0], "Mmhd") && params["paste-keep-for"][0] != "forever" {
|
||||||
|
c.Logger.Debug().Msgf("'Keep paste for' field have invalid value: %s", params["paste-keep-for"][0])
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify captcha.
|
||||||
|
if !captcha.Verify(params["paste-captcha-id"][0], params["paste-captcha-solution"][0]) {
|
||||||
|
c.Logger.Debug().Msgf("Invalid captcha solution for captcha ID '%s': %s", params["paste-captcha-id"][0], params["paste-captcha-solution"][0])
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Invalid captcha solution.")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
paste := &structs.Paste{
|
||||||
|
Title: params["paste-title"][0],
|
||||||
|
Data: params["paste-contents"][0],
|
||||||
|
Language: params["paste-language"][0],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paste creation time in UTC.
|
||||||
|
createdAt := time.Now().UTC()
|
||||||
|
paste.CreatedAt = &createdAt
|
||||||
|
|
||||||
|
// Parse "keep for" field.
|
||||||
|
// Defaulting to "forever".
|
||||||
|
keepFor := 0
|
||||||
|
keepForUnit := 0
|
||||||
|
if params["paste-keep-for"][0] != "forever" {
|
||||||
|
keepForUnitRegex := regexp.MustCompile("[Mmhd]")
|
||||||
|
|
||||||
|
keepForRaw := regexInts.FindAllString(params["paste-keep-for"][0], 1)[0]
|
||||||
|
var err error
|
||||||
|
keepFor, err = strconv.Atoi(keepForRaw)
|
||||||
|
if err != nil {
|
||||||
|
if params["paste-keep-for"][0] == "forever" {
|
||||||
|
c.Logger.Debug().Msg("Keeping paste forever!")
|
||||||
|
keepFor = 0
|
||||||
|
} else {
|
||||||
|
c.Logger.Debug().Err(err).Msg("Failed to parse 'Keep for' integer")
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keepForUnitRaw := keepForUnitRegex.FindAllString(params["paste-keep-for"][0], 1)[0]
|
||||||
|
keepForUnit = structs.PASTE_KEEPS_CORELLATION[keepForUnitRaw]
|
||||||
|
}
|
||||||
|
paste.KeepFor = keepFor
|
||||||
|
paste.KeepForUnitType = keepForUnit
|
||||||
|
|
||||||
|
// Try to autodetect if it was selected.
|
||||||
|
if params["paste-language"][0] == "autodetect" {
|
||||||
|
lexer := lexers.Analyse(params["paste-language"][0])
|
||||||
|
if lexer != nil {
|
||||||
|
paste.Language = lexer.Config().Name
|
||||||
|
} else {
|
||||||
|
paste.Language = "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private paste?
|
||||||
|
paste.Private = false
|
||||||
|
privateCheckbox, privateCheckboxFound := params["paste-private"]
|
||||||
|
pastePassword, pastePasswordFound := params["paste-password"]
|
||||||
|
if privateCheckboxFound && privateCheckbox[0] == "on" || pastePasswordFound && pastePassword[0] != "" {
|
||||||
|
paste.Private = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if pastePassword[0] != "" {
|
||||||
|
paste.CreatePassword(pastePassword[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err2 := c.Database.SavePaste(paste)
|
||||||
|
if err2 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to save paste: %s", err2.Error())
|
||||||
|
errtpl := templater.GetErrorTemplate(ec, "Failed to save paste. Please, try again later.")
|
||||||
|
return ec.HTML(http.StatusBadRequest, errtpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
newPasteIDAsString := strconv.FormatInt(id, 10)
|
||||||
|
c.Logger.Debug().Msgf("Paste saved, URL: /paste/" + newPasteIDAsString)
|
||||||
|
|
||||||
|
// Private pastes have it's timestamp in URL.
|
||||||
|
if paste.Private {
|
||||||
|
return ec.Redirect(http.StatusFound, "/paste/"+newPasteIDAsString+"/"+strconv.FormatInt(paste.CreatedAt.Unix(), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ec.Redirect(http.StatusFound, "/paste/"+newPasteIDAsString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET for "/pastes/:id/raw", raw paste output.
|
||||||
|
func pasteRawGET(ec echo.Context) error {
|
||||||
|
// We should check if database connection available.
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if c.Config.Database.Type != "flatfiles" && dbConn == nil {
|
||||||
|
return ec.Redirect(http.StatusFound, "/database_not_available/raw")
|
||||||
|
}
|
||||||
|
|
||||||
|
pasteIDRaw := ec.Param("id")
|
||||||
|
// We already get numbers from string, so we will not check strconv.Atoi()
|
||||||
|
// error.
|
||||||
|
pasteID, _ := strconv.Atoi(regexInts.FindAllString(pasteIDRaw, 1)[0])
|
||||||
|
c.Logger.Debug().Msgf("Requesting paste #%+v", pasteID)
|
||||||
|
|
||||||
|
// Get paste.
|
||||||
|
paste, err1 := c.Database.GetPaste(pasteID)
|
||||||
|
if err1 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to get paste #%d from database: %s", pasteID, err1.Error())
|
||||||
|
return ec.HTML(http.StatusBadRequest, "Paste #"+pasteIDRaw+" does not exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if paste.IsExpired() {
|
||||||
|
c.Logger.Error().Msgf("Paste #%d is expired", pasteID)
|
||||||
|
return ec.HTML(http.StatusBadRequest, "Paste #"+pasteIDRaw+" does not exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have a private paste and it's parameters are correct.
|
||||||
|
if paste.Private {
|
||||||
|
tsProvidedStr := ec.Param("timestamp")
|
||||||
|
tsProvided, err2 := strconv.ParseInt(tsProvidedStr, 10, 64)
|
||||||
|
if err2 != nil {
|
||||||
|
c.Logger.Error().Msgf("Invalid timestamp '%s' provided for getting private paste #%d: %s", tsProvidedStr, pasteID, err2.Error())
|
||||||
|
return ec.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
|
||||||
|
}
|
||||||
|
pasteTs := paste.CreatedAt.Unix()
|
||||||
|
if tsProvided != pasteTs {
|
||||||
|
c.Logger.Error().Msgf("Incorrect timestamp '%v' provided for private paste #%d, waiting for %v", tsProvidedStr, pasteID, strconv.FormatInt(pasteTs, 10))
|
||||||
|
return ec.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: figure out how to handle passworded pastes here.
|
||||||
|
// Return error for now.
|
||||||
|
if paste.Password != "" {
|
||||||
|
c.Logger.Error().Msgf("Cannot render paste #%d as raw: passworded paste. Patches welcome!", pasteID)
|
||||||
|
return ec.String(http.StatusBadRequest, "Paste #"+pasteIDRaw+" not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ec.String(http.StatusOK, paste.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET for "/pastes/", a list of publicly available pastes.
|
||||||
|
func pastesGET(ec echo.Context) error {
|
||||||
|
// We should check if database connection available.
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if c.Config.Database.Type != "flatfiles" && dbConn == nil {
|
||||||
|
return ec.Redirect(http.StatusFound, "/database_not_available")
|
||||||
|
}
|
||||||
|
|
||||||
|
pageFromParamRaw := ec.Param("page")
|
||||||
|
var page = 1
|
||||||
|
if pageFromParamRaw != "" {
|
||||||
|
pageRaw := regexInts.FindAllString(pageFromParamRaw, 1)[0]
|
||||||
|
page, _ = strconv.Atoi(pageRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Debug().Msgf("Requested page #%d", page)
|
||||||
|
|
||||||
|
// Get pastes IDs.
|
||||||
|
pastes, err3 := c.Database.GetPagedPastes(page)
|
||||||
|
c.Logger.Debug().Msgf("Got %d pastes", len(pastes))
|
||||||
|
|
||||||
|
var pastesString = "No pastes to show."
|
||||||
|
|
||||||
|
// Show "No pastes to show" on any error for now.
|
||||||
|
if err3 != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to get pastes list from database: %s", err3.Error())
|
||||||
|
noPastesToShowTpl := templater.GetErrorTemplate(ec, "No pastes to show.")
|
||||||
|
return ec.HTML(http.StatusOK, noPastesToShowTpl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pastes) > 0 {
|
||||||
|
pastesString = ""
|
||||||
|
for i := range pastes {
|
||||||
|
pasteDataMap := make(map[string]string)
|
||||||
|
pasteDataMap["pasteID"] = strconv.Itoa(pastes[i].ID)
|
||||||
|
pasteDataMap["pasteTitle"] = pastes[i].Title
|
||||||
|
pasteDataMap["pasteDate"] = pastes[i].CreatedAt.Format("2006-01-02 @ 15:04:05") + " UTC"
|
||||||
|
|
||||||
|
// Get max 4 lines of each paste.
|
||||||
|
pasteDataSplitted := strings.Split(pastes[i].Data, "\n")
|
||||||
|
var pasteData = ""
|
||||||
|
if len(pasteDataSplitted) < 4 {
|
||||||
|
pasteData = pastes[i].Data
|
||||||
|
} else {
|
||||||
|
pasteData = strings.Join(pasteDataSplitted[0:4], "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
pasteDataMap["pasteData"] = pasteData
|
||||||
|
pasteTpl := templater.GetRawTemplate(ec, "pastelist_paste.html", pasteDataMap)
|
||||||
|
|
||||||
|
pastesString += pasteTpl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination.
|
||||||
|
pages := c.Database.GetPastesPages()
|
||||||
|
c.Logger.Debug().Msgf("Total pages: %d, current: %d", pages, page)
|
||||||
|
paginationHTML := pagination.CreateHTML(page, pages, "/pastes/")
|
||||||
|
|
||||||
|
pasteListTpl := templater.GetTemplate(ec, "pastelist_list.html", map[string]string{"pastes": pastesString, "pagination": paginationHTML})
|
||||||
|
|
||||||
|
return ec.HTML(http.StatusOK, string(pasteListTpl))
|
||||||
|
}
|
60
domains/pastes/exported.go
Normal file
60
domains/pastes/exported.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package pastes
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes pastes package and adds neccessary HTTP and API
|
||||||
|
// endpoints.
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
|
||||||
|
// New paste.
|
||||||
|
c.Echo.POST("/paste/", pastePOST)
|
||||||
|
|
||||||
|
// Show public paste.
|
||||||
|
c.Echo.GET("/paste/:id", pasteGET)
|
||||||
|
// Show RAW representation of public paste.
|
||||||
|
c.Echo.GET("/paste/:id/raw", pasteRawGET)
|
||||||
|
|
||||||
|
// Show private paste.
|
||||||
|
c.Echo.GET("/paste/:id/:timestamp", pasteGET)
|
||||||
|
// Show RAW representation of private paste.
|
||||||
|
c.Echo.GET("/paste/:id/:timestamp/raw", pasteRawGET)
|
||||||
|
// Verify access to passworded paste.
|
||||||
|
c.Echo.GET("/paste/:id/:timestamp/verify", pastePasswordedVerifyGet)
|
||||||
|
c.Echo.POST("/paste/:id/:timestamp/verify", pastePasswordedVerifyPost)
|
||||||
|
|
||||||
|
// Pastes list.
|
||||||
|
c.Echo.GET("/pastes/", pastesGET)
|
||||||
|
c.Echo.GET("/pastes/:page", pastesGET)
|
||||||
|
}
|
62
internal/captcha/exported.go
Normal file
62
internal/captcha/exported.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package captcha
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/dchest/captcha"
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
log zerolog.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes captcha package and adds neccessary HTTP and API
|
||||||
|
// endpoints.
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
log = c.Logger.With().Str("type", "internal").Str("package", "captcha").Logger()
|
||||||
|
|
||||||
|
// New paste.
|
||||||
|
c.Echo.GET("/captcha/:id.png", echo.WrapHandler(captcha.Server(captcha.StdWidth, captcha.StdHeight)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCaptcha creates new captcha string.
|
||||||
|
func NewCaptcha() string {
|
||||||
|
s := captcha.New()
|
||||||
|
log.Debug().Str("captcha string", s).Msg("Created new captcha string")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify verifies captchas.
|
||||||
|
func Verify(captchaString string, captchaSolution string) bool {
|
||||||
|
return captcha.VerifyString(captchaString, captchaSolution)
|
||||||
|
}
|
36
internal/config/database.go
Normal file
36
internal/config/database.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
// ConfigDatabase describes database configuration.
|
||||||
|
type ConfigDatabase struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
Address string `yaml:"address"`
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
Database string `yaml:"database"`
|
||||||
|
}
|
32
internal/config/http.go
Normal file
32
internal/config/http.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
// ConfigHTTP describes HTTP server configuration.
|
||||||
|
type ConfigHTTP struct {
|
||||||
|
Address string `yaml:"address"`
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
AllowInsecure bool `yaml:"allow_insecure"`
|
||||||
|
}
|
32
internal/config/logging.go
Normal file
32
internal/config/logging.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
// ConfigLogging describes logger configuration.
|
||||||
|
type ConfigLogging struct {
|
||||||
|
LogToFile bool `yaml:"log_to_file"`
|
||||||
|
FileName string `yaml:"filename"`
|
||||||
|
LogLevel string `yaml:"loglevel"`
|
||||||
|
}
|
30
internal/config/pastes.go
Normal file
30
internal/config/pastes.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
// ConfigPastes describes pastes subsystem configuration.
|
||||||
|
type ConfigPastes struct {
|
||||||
|
Pagination int `yaml:"pagination"`
|
||||||
|
}
|
33
internal/config/struct.go
Normal file
33
internal/config/struct.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
// ConfigStruct describes whole configuration.
|
||||||
|
type ConfigStruct struct {
|
||||||
|
Database ConfigDatabase `yaml:"database"`
|
||||||
|
Logging ConfigLogging `yaml:"logging"`
|
||||||
|
HTTP ConfigHTTP `yaml:"http"`
|
||||||
|
Pastes ConfigPastes `yaml:"pastes"`
|
||||||
|
}
|
140
internal/context/context.go
Normal file
140
internal/context/context.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/config"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/interface"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"gitlab.com/pztrn/flagger"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context is a some sort of singleton. Basically it's a structure that
|
||||||
|
// initialized once and then passed to all parts of application. It
|
||||||
|
// contains everything every part of application need, like configuration
|
||||||
|
// access, logger, etc.
|
||||||
|
type Context struct {
|
||||||
|
Config *config.ConfigStruct
|
||||||
|
Database databaseinterface.Interface
|
||||||
|
Echo *echo.Echo
|
||||||
|
Flagger *flagger.Flagger
|
||||||
|
Logger zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes context.
|
||||||
|
func (c *Context) Initialize() {
|
||||||
|
c.initializeLogger()
|
||||||
|
|
||||||
|
c.Flagger = flagger.New("fastpastebin", nil)
|
||||||
|
c.Flagger.Initialize()
|
||||||
|
|
||||||
|
c.Flagger.AddFlag(&flagger.Flag{
|
||||||
|
Name: "config",
|
||||||
|
Description: "Configuration file path. Can be overridded with FASTPASTEBIN_CONFIG environment variable (this is what used in tests).",
|
||||||
|
Type: "string",
|
||||||
|
DefaultValue: "NO_CONFIG",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializePost initializes everything that needs a configuration.
|
||||||
|
func (c *Context) InitializePost() {
|
||||||
|
c.initializeLoggerPost()
|
||||||
|
c.initializeHTTPServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfiguration loads configuration and executes right after Flagger
|
||||||
|
// have parsed CLI flags, because it depends on "-config" defined in
|
||||||
|
// Initialize().
|
||||||
|
func (c *Context) LoadConfiguration() {
|
||||||
|
c.Logger.Info().Msg("Loading configuration...")
|
||||||
|
|
||||||
|
var configPath = ""
|
||||||
|
|
||||||
|
// We're accepting configuration path from "-config" CLI parameter
|
||||||
|
// and FASTPASTEBIN_CONFIG environment variable. Later have higher
|
||||||
|
// weight and can override "-config" value.
|
||||||
|
configPathFromCLI, err := c.Flagger.GetStringValue("config")
|
||||||
|
configPathFromEnv, configPathFromEnvFound := os.LookupEnv("FASTPASTEBIN_CONFIG")
|
||||||
|
|
||||||
|
if err != nil && configPathFromEnvFound || err == nil && configPathFromEnvFound {
|
||||||
|
configPath = configPathFromEnv
|
||||||
|
} else if err != nil && !configPathFromEnvFound || err == nil && configPathFromCLI == "NO_CONFIG" {
|
||||||
|
c.Logger.Panic().Msg("Configuration file path wasn't passed via '-config' or 'FASTPASTEBIN_CONFIG' environment variable. Cannot continue.")
|
||||||
|
} else if err == nil && !configPathFromEnvFound {
|
||||||
|
configPath = configPathFromCLI
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize file path.
|
||||||
|
normalizedConfigPath, err1 := filepath.Abs(configPath)
|
||||||
|
if err1 != nil {
|
||||||
|
c.Logger.Fatal().Msgf("Failed to normalize path to configuration file: %s", err1.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Debug().Msgf("Configuration file path: %s", configPath)
|
||||||
|
|
||||||
|
c.Config = &config.ConfigStruct{}
|
||||||
|
|
||||||
|
// Read configuration file.
|
||||||
|
fileData, err2 := ioutil.ReadFile(normalizedConfigPath)
|
||||||
|
if err2 != nil {
|
||||||
|
c.Logger.Panic().Msgf("Failed to read configuration file: %s", err2.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse it into structure.
|
||||||
|
err3 := yaml.Unmarshal(fileData, c.Config)
|
||||||
|
if err3 != nil {
|
||||||
|
c.Logger.Panic().Msgf("Failed to parse configuration file: %s", err3.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yay! See what it gets!
|
||||||
|
c.Logger.Debug().Msgf("Parsed configuration: %+v", c.Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterDatabaseInterface registers database interface for later use.
|
||||||
|
func (c *Context) RegisterDatabaseInterface(di databaseinterface.Interface) {
|
||||||
|
c.Database = di
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterEcho registers Echo instance for later usage.
|
||||||
|
func (c *Context) RegisterEcho(e *echo.Echo) {
|
||||||
|
c.Echo = e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown shutdowns entire application.
|
||||||
|
func (c *Context) Shutdown() {
|
||||||
|
c.Logger.Info().Msg("Shutting down Fast Pastebin...")
|
||||||
|
|
||||||
|
c.Database.Shutdown()
|
||||||
|
}
|
35
internal/context/exported.go
Normal file
35
internal/context/exported.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package context
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Version .
|
||||||
|
Version = "0.3.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates new context.
|
||||||
|
func New() *Context {
|
||||||
|
return &Context{}
|
||||||
|
}
|
47
internal/context/http_server.go
Normal file
47
internal/context/http_server.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/assets/static"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/labstack/echo/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Context) initializeHTTPServer() {
|
||||||
|
c.Echo = echo.New()
|
||||||
|
c.Echo.Use(c.echoReqLogger())
|
||||||
|
c.Echo.Use(middleware.Recover())
|
||||||
|
c.Echo.DisableHTTP2 = true
|
||||||
|
c.Echo.HideBanner = true
|
||||||
|
c.Echo.HidePort = true
|
||||||
|
|
||||||
|
// Static files.
|
||||||
|
c.Echo.GET("/static/*", echo.WrapHandler(static.Handler))
|
||||||
|
|
||||||
|
listenAddress := c.Config.HTTP.Address + ":" + c.Config.HTTP.Port
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
c.Echo.Logger.Fatal(c.Echo.Start(listenAddress))
|
||||||
|
}()
|
||||||
|
c.Logger.Info().Msgf("Started HTTP server at %s", listenAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper around previous function.
|
||||||
|
func (c *Context) echoReqLogger() echo.MiddlewareFunc {
|
||||||
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(ec echo.Context) error {
|
||||||
|
c.Logger.Info().
|
||||||
|
Str("IP", ec.RealIP()).
|
||||||
|
Str("Host", ec.Request().Host).
|
||||||
|
Str("Method", ec.Request().Method).
|
||||||
|
Str("Path", ec.Request().URL.Path).
|
||||||
|
Str("UA", ec.Request().UserAgent()).
|
||||||
|
Msg("HTTP request")
|
||||||
|
|
||||||
|
next(ec)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
internal/context/logger.go
Normal file
76
internal/context/logger.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Puts memory usage into log lines.
|
||||||
|
func (c *Context) getMemoryUsage(e *zerolog.Event, level zerolog.Level, message string) {
|
||||||
|
var m runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&m)
|
||||||
|
|
||||||
|
e.Str("memalloc", fmt.Sprintf("%dMB", m.Alloc/1024/1024))
|
||||||
|
e.Str("memsys", fmt.Sprintf("%dMB", m.Sys/1024/1024))
|
||||||
|
e.Str("numgc", fmt.Sprintf("%d", m.NumGC))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializes logger.
|
||||||
|
func (c *Context) initializeLogger() {
|
||||||
|
// Устанавливаем форматирование логгера.
|
||||||
|
output := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: false, TimeFormat: time.RFC3339}
|
||||||
|
output.FormatLevel = func(i interface{}) string {
|
||||||
|
var v string
|
||||||
|
if ii, ok := i.(string); ok {
|
||||||
|
ii = strings.ToUpper(ii)
|
||||||
|
switch ii {
|
||||||
|
case "DEBUG":
|
||||||
|
v = fmt.Sprintf("\x1b[30m%-5s\x1b[0m", ii)
|
||||||
|
case "ERROR":
|
||||||
|
v = fmt.Sprintf("\x1b[31m%-5s\x1b[0m", ii)
|
||||||
|
case "FATAL":
|
||||||
|
v = fmt.Sprintf("\x1b[35m%-5s\x1b[0m", ii)
|
||||||
|
case "INFO":
|
||||||
|
v = fmt.Sprintf("\x1b[32m%-5s\x1b[0m", ii)
|
||||||
|
case "PANIC":
|
||||||
|
v = fmt.Sprintf("\x1b[36m%-5s\x1b[0m", ii)
|
||||||
|
case "WARN":
|
||||||
|
v = fmt.Sprintf("\x1b[33m%-5s\x1b[0m", ii)
|
||||||
|
default:
|
||||||
|
v = ii
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("| %s |", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger = zerolog.New(output).With().Timestamp().Logger()
|
||||||
|
|
||||||
|
c.Logger = c.Logger.Hook(zerolog.HookFunc(c.getMemoryUsage))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize logger after configuration parse.
|
||||||
|
func (c *Context) initializeLoggerPost() {
|
||||||
|
// Set log level.
|
||||||
|
c.Logger.Info().Msgf("Setting logger level: %s", c.Config.Logging.LogLevel)
|
||||||
|
switch c.Config.Logging.LogLevel {
|
||||||
|
case "DEBUG":
|
||||||
|
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||||
|
case "INFO":
|
||||||
|
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||||
|
case "WARN":
|
||||||
|
zerolog.SetGlobalLevel(zerolog.WarnLevel)
|
||||||
|
case "ERROR":
|
||||||
|
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
|
||||||
|
case "FATAL":
|
||||||
|
zerolog.SetGlobalLevel(zerolog.FatalLevel)
|
||||||
|
case "PANIC":
|
||||||
|
zerolog.SetGlobalLevel(zerolog.PanicLevel)
|
||||||
|
}
|
||||||
|
}
|
93
internal/database/database.go
Normal file
93
internal/database/database.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/flatfiles"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/interface"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/mysql"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/postgresql"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
|
||||||
|
// other
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Database represents control structure for database connection.
|
||||||
|
type Database struct {
|
||||||
|
db dialectinterface.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetDatabaseConnection() *sql.DB {
|
||||||
|
if db.db != nil {
|
||||||
|
return db.db.GetDatabaseConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
return db.db.GetPaste(pasteID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
return db.db.GetPagedPastes(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPastesPages() int {
|
||||||
|
return db.db.GetPastesPages()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes connection to database.
|
||||||
|
func (db *Database) Initialize() {
|
||||||
|
c.Logger.Info().Msg("Initializing database connection...")
|
||||||
|
|
||||||
|
if c.Config.Database.Type == "mysql" {
|
||||||
|
mysql.New(c)
|
||||||
|
} else if c.Config.Database.Type == "flatfiles" {
|
||||||
|
flatfiles.New(c)
|
||||||
|
} else if c.Config.Database.Type == "postgresql" {
|
||||||
|
postgresql.New(c)
|
||||||
|
} else {
|
||||||
|
c.Logger.Fatal().Msgf("Unknown database type: %s", c.Config.Database.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) RegisterDialect(di dialectinterface.Interface) {
|
||||||
|
db.db = di
|
||||||
|
db.db.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
return db.db.SavePaste(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) Shutdown() {
|
||||||
|
db.db.Shutdown()
|
||||||
|
}
|
42
internal/database/dialects/flatfiles/exported.go
Normal file
42
internal/database/dialects/flatfiles/exported.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package flatfiles
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
f *FlatFiles
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
f = &FlatFiles{}
|
||||||
|
c.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
|
||||||
|
}
|
246
internal/database/dialects/flatfiles/flatfiles.go
Normal file
246
internal/database/dialects/flatfiles/flatfiles.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package flatfiles
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlatFiles struct {
|
||||||
|
pastesIndex []*Index
|
||||||
|
path string
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ff *FlatFiles) GetDatabaseConnection() *sql.DB {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ff *FlatFiles) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
ff.writeMutex.Lock()
|
||||||
|
pastePath := filepath.Join(ff.path, "pastes", strconv.Itoa(pasteID)+".json")
|
||||||
|
c.Logger.Debug().Msgf("Trying to load paste data from '%s'...", pastePath)
|
||||||
|
pasteInBytes, err := ioutil.ReadFile(pastePath)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Debug().Msgf("Failed to read paste from storage: %s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.Logger.Debug().Msgf("Loaded %d bytes: %s", len(pasteInBytes), string(pasteInBytes))
|
||||||
|
ff.writeMutex.Unlock()
|
||||||
|
|
||||||
|
paste := &structs.Paste{}
|
||||||
|
err = json.Unmarshal(pasteInBytes, paste)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to parse paste: %s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return paste, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ff *FlatFiles) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
// Pagination.
|
||||||
|
var startPagination = 0
|
||||||
|
if page > 1 {
|
||||||
|
startPagination = (page - 1) * c.Config.Pastes.Pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Debug().Msgf("Pastes index: %+v", ff.pastesIndex)
|
||||||
|
|
||||||
|
// Iteration one - get only public pastes.
|
||||||
|
var publicPastes []*Index
|
||||||
|
for _, paste := range ff.pastesIndex {
|
||||||
|
if !paste.Private {
|
||||||
|
publicPastes = append(publicPastes, paste)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Debug().Msgf("%+v", publicPastes)
|
||||||
|
|
||||||
|
// Iteration two - get paginated pastes.
|
||||||
|
var pastesData []structs.Paste
|
||||||
|
for idx, paste := range publicPastes {
|
||||||
|
if len(pastesData) == c.Config.Pastes.Pagination {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx < startPagination {
|
||||||
|
c.Logger.Debug().Msgf("Paste with index %d isn't in pagination query: too low index", idx)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx-1 >= startPagination && page > 1 && idx > startPagination+((page-1)*c.Config.Pastes.Pagination)) || (idx-1 >= startPagination && page == 1 && idx > startPagination+(page*c.Config.Pastes.Pagination)) {
|
||||||
|
c.Logger.Debug().Msgf("Paste with index %d isn't in pagination query: too high index", idx)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c.Logger.Debug().Msgf("Getting paste data (ID: %d, index: %d)", paste.ID, idx)
|
||||||
|
|
||||||
|
// Get paste data.
|
||||||
|
pasteData := &structs.Paste{}
|
||||||
|
pasteRawData, err := ioutil.ReadFile(filepath.Join(ff.path, "pastes", strconv.Itoa(paste.ID)+".json"))
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to read paste data: %s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(pasteRawData, pasteData)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to parse paste data: %s", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pastesData = append(pastesData, (*pasteData))
|
||||||
|
}
|
||||||
|
|
||||||
|
return pastesData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ff *FlatFiles) GetPastesPages() int {
|
||||||
|
// Get public pastes count.
|
||||||
|
var publicPastes []*Index
|
||||||
|
|
||||||
|
ff.writeMutex.Lock()
|
||||||
|
for _, paste := range ff.pastesIndex {
|
||||||
|
if !paste.Private {
|
||||||
|
publicPastes = append(publicPastes, paste)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ff.writeMutex.Unlock()
|
||||||
|
|
||||||
|
// Calculate pages.
|
||||||
|
pages := len(publicPastes) / c.Config.Pastes.Pagination
|
||||||
|
// Check if we have any remainder. Add 1 to pages count if so.
|
||||||
|
if len(publicPastes)%c.Config.Pastes.Pagination > 0 {
|
||||||
|
pages++
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ff *FlatFiles) Initialize() {
|
||||||
|
c.Logger.Info().Msg("Initializing flatfiles storage...")
|
||||||
|
|
||||||
|
path := c.Config.Database.Path
|
||||||
|
// Get proper paste file path.
|
||||||
|
if strings.Contains(c.Config.Database.Path, "~") {
|
||||||
|
curUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msg("Failed to get current user. Will replace '~' for '/' in storage path!")
|
||||||
|
path = strings.Replace(path, "~", "/", -1)
|
||||||
|
}
|
||||||
|
path = strings.Replace(path, "~", curUser.HomeDir, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
path, _ = filepath.Abs(path)
|
||||||
|
ff.path = path
|
||||||
|
c.Logger.Debug().Msgf("Storage path is now: %s", ff.path)
|
||||||
|
|
||||||
|
// Create directory if neccessary.
|
||||||
|
if _, err := os.Stat(ff.path); err != nil {
|
||||||
|
c.Logger.Debug().Msgf("Directory '%s' does not exist, creating...", ff.path)
|
||||||
|
os.MkdirAll(ff.path, os.ModePerm)
|
||||||
|
} else {
|
||||||
|
c.Logger.Debug().Msgf("Directory '%s' already exists", ff.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create directory for pastes.
|
||||||
|
if _, err := os.Stat(filepath.Join(ff.path, "pastes")); err != nil {
|
||||||
|
c.Logger.Debug().Msgf("Directory '%s' does not exist, creating...", filepath.Join(ff.path, "pastes"))
|
||||||
|
os.MkdirAll(filepath.Join(ff.path, "pastes"), os.ModePerm)
|
||||||
|
} else {
|
||||||
|
c.Logger.Debug().Msgf("Directory '%s' already exists", filepath.Join(ff.path, "pastes"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load pastes index.
|
||||||
|
ff.pastesIndex = []*Index{}
|
||||||
|
if _, err := os.Stat(filepath.Join(ff.path, "pastes", "index.json")); err != nil {
|
||||||
|
c.Logger.Warn().Msg("Pastes index file does not exist, will create new one")
|
||||||
|
} else {
|
||||||
|
indexData, err := ioutil.ReadFile(filepath.Join(ff.path, "pastes", "index.json"))
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Fatal().Msg("Failed to read contents of index file!")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(indexData, &ff.pastesIndex)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to parse index file contents from JSON into internal structure. Will create new index file. All of your previous pastes will became unavailable. Error was: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Debug().Msgf("Parsed pastes index: %+v", ff.pastesIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ff *FlatFiles) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
ff.writeMutex.Lock()
|
||||||
|
// Write paste data on disk.
|
||||||
|
filesOnDisk, _ := ioutil.ReadDir(filepath.Join(ff.path, "pastes"))
|
||||||
|
pasteID := len(filesOnDisk) + 1
|
||||||
|
c.Logger.Debug().Msgf("Writing paste to disk, ID will be " + strconv.Itoa(pasteID))
|
||||||
|
p.ID = pasteID
|
||||||
|
data, err := json.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
ff.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(ff.path, "pastes", strconv.Itoa(pasteID)+".json"), data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
ff.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Add it to cache.
|
||||||
|
indexData := &Index{}
|
||||||
|
indexData.ID = pasteID
|
||||||
|
indexData.Private = p.Private
|
||||||
|
ff.pastesIndex = append(ff.pastesIndex, indexData)
|
||||||
|
ff.writeMutex.Unlock()
|
||||||
|
return int64(pasteID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ff *FlatFiles) Shutdown() {
|
||||||
|
c.Logger.Info().Msg("Saving indexes...")
|
||||||
|
indexData, err := json.Marshal(ff.pastesIndex)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to encode index data into JSON: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(ff.path, "pastes", "index.json"), indexData, 0644)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to write index data to file. Pretty sure that you've lost your pastes.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
63
internal/database/dialects/flatfiles/handler.go
Normal file
63
internal/database/dialects/flatfiles/handler.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package flatfiles
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct{}
|
||||||
|
|
||||||
|
func (dbh Handler) GetDatabaseConnection() *sql.DB {
|
||||||
|
return f.GetDatabaseConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
return f.GetPaste(pasteID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
return f.GetPagedPastes(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPastesPages() int {
|
||||||
|
return f.GetPastesPages()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) Initialize() {
|
||||||
|
f.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
return f.SavePaste(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) Shutdown() {
|
||||||
|
f.Shutdown()
|
||||||
|
}
|
31
internal/database/dialects/flatfiles/index.go
Normal file
31
internal/database/dialects/flatfiles/index.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package flatfiles
|
||||||
|
|
||||||
|
// Index describes paste index structure for flatfiles data storage.
|
||||||
|
type Index struct {
|
||||||
|
ID int `yaml:"id"`
|
||||||
|
Private bool `json:"private"`
|
||||||
|
}
|
43
internal/database/dialects/interface/dialectinterface.go
Normal file
43
internal/database/dialects/interface/dialectinterface.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package dialectinterface
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
GetDatabaseConnection() *sql.DB
|
||||||
|
GetPaste(pasteID int) (*structs.Paste, error)
|
||||||
|
GetPagedPastes(page int) ([]structs.Paste, error)
|
||||||
|
GetPastesPages() int
|
||||||
|
Initialize()
|
||||||
|
SavePaste(p *structs.Paste) (int64, error)
|
||||||
|
Shutdown()
|
||||||
|
}
|
42
internal/database/dialects/mysql/exported.go
Normal file
42
internal/database/dialects/mysql/exported.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
d *Database
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
d = &Database{}
|
||||||
|
c.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
|
||||||
|
}
|
63
internal/database/dialects/mysql/handler.go
Normal file
63
internal/database/dialects/mysql/handler.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct{}
|
||||||
|
|
||||||
|
func (dbh Handler) GetDatabaseConnection() *sql.DB {
|
||||||
|
return d.GetDatabaseConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
return d.GetPaste(pasteID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
return d.GetPagedPastes(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPastesPages() int {
|
||||||
|
return d.GetPastesPages()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) Initialize() {
|
||||||
|
d.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
return d.SavePaste(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) Shutdown() {
|
||||||
|
d.Shutdown()
|
||||||
|
}
|
39
internal/database/dialects/mysql/migrations/1_initial.go
Normal file
39
internal/database/dialects/mysql/migrations/1_initial.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitialUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("CREATE TABLE `pastes` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Paste ID', `title` text NOT NULL COMMENT 'Paste title', `data` longtext NOT NULL COMMENT 'Paste data', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Paste creation timestamp', `keep_for` int(4) NOT NULL DEFAULT 1 COMMENT 'Keep for integer. 0 - forever.', `keep_for_unit_type` int(1) NOT NULL DEFAULT 1 COMMENT 'Keep for unit type. 1 - minutes, 2 - hours, 3 - days, 4 - months.', PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Pastes';")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
48
internal/database/dialects/mysql/migrations/2_paste_lang.go
Normal file
48
internal/database/dialects/mysql/migrations/2_paste_lang.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PasteLangUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE `pastes` ADD `language` VARCHAR(191) NOT NULL DEFAULT 'text' COMMENT 'Paste language'")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PasteLangDown(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `language`")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrivatePastesUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE `pastes` ADD `private` BOOL NOT NULL DEFAULT false COMMENT 'Private paste? If true - additional URL parameter (UNIX TIMESTAMP) of paste will be required to access.'")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrivatePastesDown(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `private`")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PasswordedPastesUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE `pastes` ADD `password` varchar(64) NOT NULL DEFAULT '' COMMENT 'Password for paste (scrypted and sha256ed).'")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err1 := tx.Exec("ALTER TABLE `pastes` ADD `password_salt` varchar(64) NOT NULL DEFAULT '' COMMENT 'Password salt (sha256ed).'")
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PasswordedPastesDown(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `password`")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err1 := tx.Exec("ALTER TABLE `pastes` DROP COLUMN `password_salt`")
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
65
internal/database/dialects/mysql/migrations/exported.go
Normal file
65
internal/database/dialects/mysql/migrations/exported.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
|
||||||
|
// other
|
||||||
|
//"gitlab.com/jmoiron/sqlx"
|
||||||
|
"github.com/pressly/goose"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes migrations.
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate launching migrations.
|
||||||
|
func Migrate() {
|
||||||
|
c.Logger.Info().Msg("Migrating database...")
|
||||||
|
|
||||||
|
goose.SetDialect("mysql")
|
||||||
|
goose.AddNamedMigration("1_initial.go", InitialUp, nil)
|
||||||
|
goose.AddNamedMigration("2_paste_lang.go", PasteLangUp, PasteLangDown)
|
||||||
|
goose.AddNamedMigration("3_private_pastes.go", PrivatePastesUp, PrivatePastesDown)
|
||||||
|
goose.AddNamedMigration("4_passworded_pastes.go", PasswordedPastesUp, PasswordedPastesDown)
|
||||||
|
// Add new migrations BEFORE this message.
|
||||||
|
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if dbConn != nil {
|
||||||
|
err := goose.Up(dbConn, ".")
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Panic().Msgf("Failed to migrate database to latest version: %s", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.Logger.Warn().Msg("Current database dialect isn't supporting migrations, skipping")
|
||||||
|
}
|
||||||
|
}
|
187
internal/database/dialects/mysql/mysqldatabase.go
Normal file
187
internal/database/dialects/mysql/mysqldatabase.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/mysql/migrations"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
|
||||||
|
// other
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Database is a MySQL/MariaDB connection controlling structure.
|
||||||
|
type Database struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if queries can be run on known connection and reestablish it
|
||||||
|
// if not.
|
||||||
|
func (db *Database) check() {
|
||||||
|
if db.db != nil {
|
||||||
|
_, err := db.db.Exec("SELECT 1")
|
||||||
|
if err != nil {
|
||||||
|
db.Initialize()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
db.Initialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetDatabaseConnection() *sql.DB {
|
||||||
|
db.check()
|
||||||
|
|
||||||
|
if db.db != nil {
|
||||||
|
return db.db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPaste returns a single paste by ID.
|
||||||
|
func (db *Database) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
db.check()
|
||||||
|
p := &structs.Paste{}
|
||||||
|
err := db.db.Get(p, db.db.Rebind("SELECT * FROM `pastes` WHERE id=?"), pasteID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
db.check()
|
||||||
|
var pastesRaw []structs.Paste
|
||||||
|
var pastes []structs.Paste
|
||||||
|
|
||||||
|
// Pagination.
|
||||||
|
var startPagination = 0
|
||||||
|
if page > 1 {
|
||||||
|
startPagination = (page - 1) * c.Config.Pastes.Pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.db.Select(&pastesRaw, db.db.Rebind("SELECT * FROM `pastes` WHERE private != true ORDER BY id DESC LIMIT ? OFFSET ?"), c.Config.Pastes.Pagination, startPagination)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range pastesRaw {
|
||||||
|
if !pastesRaw[i].IsExpired() {
|
||||||
|
pastes = append(pastes, pastesRaw[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pastes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPastesPages() int {
|
||||||
|
db.check()
|
||||||
|
var pastesRaw []structs.Paste
|
||||||
|
var pastes []structs.Paste
|
||||||
|
err := db.db.Get(&pastesRaw, "SELECT * FROM `pastes` WHERE private != true")
|
||||||
|
if err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if pastes isn't expired.
|
||||||
|
for i := range pastesRaw {
|
||||||
|
if !pastesRaw[i].IsExpired() {
|
||||||
|
pastes = append(pastes, pastesRaw[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pages.
|
||||||
|
pages := len(pastes) / c.Config.Pastes.Pagination
|
||||||
|
// Check if we have any remainder. Add 1 to pages count if so.
|
||||||
|
if len(pastes)%c.Config.Pastes.Pagination > 0 {
|
||||||
|
pages++
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes MySQL/MariaDB connection.
|
||||||
|
func (db *Database) Initialize() {
|
||||||
|
c.Logger.Info().Msg("Initializing database connection...")
|
||||||
|
|
||||||
|
// There might be only user, without password. MySQL/MariaDB driver
|
||||||
|
// in DSN wants "user" or "user:password", "user:" is invalid.
|
||||||
|
var userpass = ""
|
||||||
|
if c.Config.Database.Password == "" {
|
||||||
|
userpass = c.Config.Database.Username
|
||||||
|
} else {
|
||||||
|
userpass = c.Config.Database.Username + ":" + c.Config.Database.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
dbConnString := fmt.Sprintf("%s@tcp(%s:%s)/%s?parseTime=true&collation=utf8mb4_unicode_ci&charset=utf8mb4", userpass, c.Config.Database.Address, c.Config.Database.Port, c.Config.Database.Database)
|
||||||
|
c.Logger.Debug().Msgf("Database connection string: %s", dbConnString)
|
||||||
|
|
||||||
|
dbConn, err := sqlx.Connect("mysql", dbConnString)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to connect to database: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force UTC for current connection.
|
||||||
|
_ = dbConn.MustExec("SET @@session.time_zone='+00:00';")
|
||||||
|
|
||||||
|
c.Logger.Info().Msg("Database connection established")
|
||||||
|
db.db = dbConn
|
||||||
|
|
||||||
|
// Perform migrations.
|
||||||
|
migrations.New(c)
|
||||||
|
migrations.Migrate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
db.check()
|
||||||
|
result, err := db.db.NamedExec("INSERT INTO `pastes` (title, data, created_at, keep_for, keep_for_unit_type, language, private, password, password_salt) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type, :language, :private, :password, :password_salt)", p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ID, err1 := result.LastInsertId()
|
||||||
|
if err1 != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) Shutdown() {
|
||||||
|
if db.db != nil {
|
||||||
|
err := db.db.Close()
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to close database connection: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
internal/database/dialects/postgresql/exported.go
Normal file
42
internal/database/dialects/postgresql/exported.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package postgresql
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
d *Database
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
d = &Database{}
|
||||||
|
c.Database.RegisterDialect(dialectinterface.Interface(Handler{}))
|
||||||
|
}
|
63
internal/database/dialects/postgresql/handler.go
Normal file
63
internal/database/dialects/postgresql/handler.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package postgresql
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct{}
|
||||||
|
|
||||||
|
func (dbh Handler) GetDatabaseConnection() *sql.DB {
|
||||||
|
return d.GetDatabaseConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
return d.GetPaste(pasteID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
return d.GetPagedPastes(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPastesPages() int {
|
||||||
|
return d.GetPastesPages()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) Initialize() {
|
||||||
|
d.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
return d.SavePaste(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) Shutdown() {
|
||||||
|
d.Shutdown()
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitialUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec(`
|
||||||
|
CREATE TABLE pastes
|
||||||
|
(
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
data TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
|
keep_for INTEGER NOT NULL DEFAULT 1,
|
||||||
|
keep_for_unit_type SMALLINT NOT NULL DEFAULT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN pastes.id IS 'Paste ID';
|
||||||
|
COMMENT ON COLUMN pastes.title IS 'Paste title';
|
||||||
|
COMMENT ON COLUMN pastes.data IS 'Paste data';
|
||||||
|
COMMENT ON COLUMN pastes.created_at IS 'Paste creation timestamp';
|
||||||
|
COMMENT ON COLUMN pastes.keep_for IS 'Keep for integer. 0 - forever.';
|
||||||
|
COMMENT ON COLUMN pastes.keep_for_unit_type IS 'Keep for unit type. 0 - forever, 1 - minutes, 2 - hours, 3 - days, 4 - months.';
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PasteLangUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE pastes ADD COLUMN language VARCHAR(191) NOT NULL DEFAULT 'text'; COMMENT ON COLUMN pastes.language IS 'Paste language';")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PasteLangDown(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE pastes DROP COLUMN language")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrivatePastesUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE pastes ADD COLUMN private BOOLEAN NOT NULL DEFAULT false; COMMENT ON COLUMN pastes.private IS 'Private paste? If true - additional URL parameter (UNIX TIMESTAMP) of paste will be required to access.';")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrivatePastesDown(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE pastes DROP COLUMN private")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PasswordedPastesUp(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE pastes ADD COLUMN password VARCHAR(64) NOT NULL DEFAULT ''; COMMENT ON COLUMN pastes.password IS 'Password for paste (scrypted and sha256ed).';")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err1 := tx.Exec("ALTER TABLE pastes ADD COLUMN password_salt VARCHAR(64) NOT NULL DEFAULT ''; COMMENT ON COLUMN pastes.password_salt IS 'Password salt (sha256ed).';")
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PasswordedPastesDown(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("ALTER TABLE pastes DROP COLUMN password")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err1 := tx.Exec("ALTER TABLE pastes DROP COLUMN password_salt")
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
66
internal/database/dialects/postgresql/migrations/exported.go
Normal file
66
internal/database/dialects/postgresql/migrations/exported.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
|
||||||
|
// other
|
||||||
|
//"gitlab.com/jmoiron/sqlx"
|
||||||
|
"github.com/pressly/goose"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes migrations.
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate launching migrations.
|
||||||
|
func Migrate() {
|
||||||
|
c.Logger.Info().Msg("Migrating database...")
|
||||||
|
|
||||||
|
goose.SetDialect("postgres")
|
||||||
|
goose.AddNamedMigration("1_initial.go", InitialUp, nil)
|
||||||
|
goose.AddNamedMigration("2_paste_lang.go", PasteLangUp, PasteLangDown)
|
||||||
|
goose.AddNamedMigration("3_private_pastes.go", PrivatePastesUp, PrivatePastesDown)
|
||||||
|
goose.AddNamedMigration("4_passworded_pastes.go", PasswordedPastesUp, PasswordedPastesDown)
|
||||||
|
// Add new migrations BEFORE this message.
|
||||||
|
|
||||||
|
dbConn := c.Database.GetDatabaseConnection()
|
||||||
|
if dbConn != nil {
|
||||||
|
err := goose.Up(dbConn, ".")
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Info().Msgf("%+v", err)
|
||||||
|
c.Logger.Panic().Msgf("Failed to migrate database to latest version: %s", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.Logger.Warn().Msg("Current database dialect isn't supporting migrations, skipping")
|
||||||
|
}
|
||||||
|
}
|
197
internal/database/dialects/postgresql/postgresqldatabase.go
Normal file
197
internal/database/dialects/postgresql/postgresqldatabase.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package postgresql
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/postgresql/migrations"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Database is a PostgreSQL connection controlling structure.
|
||||||
|
type Database struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if queries can be run on known connection and reestablish it
|
||||||
|
// if not.
|
||||||
|
func (db *Database) check() {
|
||||||
|
if db.db != nil {
|
||||||
|
_, err := db.db.Exec("SELECT 1")
|
||||||
|
if err != nil {
|
||||||
|
db.Initialize()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
db.Initialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetDatabaseConnection() *sql.DB {
|
||||||
|
db.check()
|
||||||
|
|
||||||
|
if db.db != nil {
|
||||||
|
return db.db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPaste returns a single paste by ID.
|
||||||
|
func (db *Database) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
db.check()
|
||||||
|
p := &structs.Paste{}
|
||||||
|
err := db.db.Get(p, db.db.Rebind("SELECT * FROM pastes WHERE id=$1"), pasteID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're aware of timezone in PostgreSQL, so SELECT will return
|
||||||
|
// timestamps in server's local timezone. We should convert them.
|
||||||
|
loc, _ := time.LoadLocation("UTC")
|
||||||
|
|
||||||
|
utcCreatedAt := p.CreatedAt.In(loc)
|
||||||
|
p.CreatedAt = &utcCreatedAt
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
db.check()
|
||||||
|
var pastesRaw []structs.Paste
|
||||||
|
var pastes []structs.Paste
|
||||||
|
|
||||||
|
// Pagination.
|
||||||
|
var startPagination = 0
|
||||||
|
if page > 1 {
|
||||||
|
startPagination = (page - 1) * c.Config.Pastes.Pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.db.Select(&pastesRaw, db.db.Rebind("SELECT * FROM pastes WHERE private != true ORDER BY id DESC LIMIT $1 OFFSET $2"), c.Config.Pastes.Pagination, startPagination)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're aware of timezone in PostgreSQL, so SELECT will return
|
||||||
|
// timestamps in server's local timezone. We should convert them.
|
||||||
|
loc, _ := time.LoadLocation("UTC")
|
||||||
|
|
||||||
|
for _, paste := range pastesRaw {
|
||||||
|
if !paste.IsExpired() {
|
||||||
|
utcCreatedAt := paste.CreatedAt.In(loc)
|
||||||
|
paste.CreatedAt = &utcCreatedAt
|
||||||
|
pastes = append(pastes, paste)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pastes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetPastesPages() int {
|
||||||
|
db.check()
|
||||||
|
var pastesRaw []structs.Paste
|
||||||
|
var pastes []structs.Paste
|
||||||
|
err := db.db.Get(&pastesRaw, "SELECT * FROM pastes WHERE private != true")
|
||||||
|
if err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if pastes isn't expired.
|
||||||
|
for _, paste := range pastesRaw {
|
||||||
|
if !paste.IsExpired() {
|
||||||
|
pastes = append(pastes, paste)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pages.
|
||||||
|
pages := len(pastes) / c.Config.Pastes.Pagination
|
||||||
|
// Check if we have any remainder. Add 1 to pages count if so.
|
||||||
|
if len(pastes)%c.Config.Pastes.Pagination > 0 {
|
||||||
|
pages++
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes MySQL/MariaDB connection.
|
||||||
|
func (db *Database) Initialize() {
|
||||||
|
c.Logger.Info().Msg("Initializing database connection...")
|
||||||
|
|
||||||
|
var userpass = ""
|
||||||
|
if c.Config.Database.Password == "" {
|
||||||
|
userpass = c.Config.Database.Username
|
||||||
|
} else {
|
||||||
|
userpass = c.Config.Database.Username + ":" + c.Config.Database.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
dbConnString := fmt.Sprintf("postgres://%s@%s:%s/%s?connect_timeout=10&fallback_application_name=fastpastebin&sslmode=disable", userpass, c.Config.Database.Address, c.Config.Database.Port, c.Config.Database.Database)
|
||||||
|
c.Logger.Debug().Msgf("Database connection string: %s", dbConnString)
|
||||||
|
|
||||||
|
dbConn, err := sqlx.Connect("postgres", dbConnString)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to connect to database: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Info().Msg("Database connection established")
|
||||||
|
db.db = dbConn
|
||||||
|
|
||||||
|
// Perform migrations.
|
||||||
|
migrations.New(c)
|
||||||
|
migrations.Migrate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
db.check()
|
||||||
|
stmt, err := db.db.PrepareNamed("INSERT INTO pastes (title, data, created_at, keep_for, keep_for_unit_type, language, private, password, password_salt) VALUES (:title, :data, :created_at, :keep_for, :keep_for_unit_type, :language, :private, :password, :password_salt) RETURNING id")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var id int64
|
||||||
|
err = stmt.Get(&id, p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) Shutdown() {
|
||||||
|
if db.db != nil {
|
||||||
|
err := db.db.Close()
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Error().Msgf("Failed to close database connection: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
internal/database/exported.go
Normal file
43
internal/database/exported.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
d *Database
|
||||||
|
)
|
||||||
|
|
||||||
|
// New initializes database structure.
|
||||||
|
func New(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
d = &Database{}
|
||||||
|
c.RegisterDatabaseInterface(databaseinterface.Interface(Handler{}))
|
||||||
|
}
|
71
internal/database/handler.go
Normal file
71
internal/database/handler.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/interface"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is an interfaceable structure that proxifies calls from anyone
|
||||||
|
// to Database structure.
|
||||||
|
type Handler struct{}
|
||||||
|
|
||||||
|
func (dbh Handler) GetDatabaseConnection() *sql.DB {
|
||||||
|
return d.GetDatabaseConnection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPaste(pasteID int) (*structs.Paste, error) {
|
||||||
|
return d.GetPaste(pasteID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPagedPastes(page int) ([]structs.Paste, error) {
|
||||||
|
return d.GetPagedPastes(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) GetPastesPages() int {
|
||||||
|
return d.GetPastesPages()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes connection to database.
|
||||||
|
func (dbh Handler) Initialize() {
|
||||||
|
d.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) RegisterDialect(di dialectinterface.Interface) {
|
||||||
|
d.RegisterDialect(di)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) SavePaste(p *structs.Paste) (int64, error) {
|
||||||
|
return d.SavePaste(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbh Handler) Shutdown() {
|
||||||
|
d.Shutdown()
|
||||||
|
}
|
47
internal/database/interface/databaseinterface.go
Normal file
47
internal/database/interface/databaseinterface.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package databaseinterface
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/database/dialects/interface"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface represents database interface which is available to all
|
||||||
|
// parts of application and registers with context.Context.
|
||||||
|
type Interface interface {
|
||||||
|
GetDatabaseConnection() *sql.DB
|
||||||
|
GetPaste(pasteID int) (*structs.Paste, error)
|
||||||
|
GetPagedPastes(page int) ([]structs.Paste, error)
|
||||||
|
GetPastesPages() int
|
||||||
|
Initialize()
|
||||||
|
RegisterDialect(dialectinterface.Interface)
|
||||||
|
SavePaste(p *structs.Paste) (int64, error)
|
||||||
|
Shutdown()
|
||||||
|
}
|
93
internal/pagination/exported.go
Normal file
93
internal/pagination/exported.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package pagination
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/assets/static"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateHTML creates pagination HTML based on passed parameters.
|
||||||
|
func CreateHTML(currentPage int, pages int, linksBase string) string {
|
||||||
|
// Load templates.
|
||||||
|
paginationHTMLRaw, err := static.ReadFile("pagination.html")
|
||||||
|
if err != nil {
|
||||||
|
return "Missing pagination.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
paginationLinkRaw, err1 := static.ReadFile("pagination_link.html")
|
||||||
|
if err1 != nil {
|
||||||
|
return "Missing pagination_link.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
paginationLinkCurrentRaw, err2 := static.ReadFile("pagination_link_current.html")
|
||||||
|
if err2 != nil {
|
||||||
|
return "Missing pagination_link_current.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
paginationEllipsisRaw, err3 := static.ReadFile("pagination_ellipsis.html")
|
||||||
|
if err3 != nil {
|
||||||
|
return "Missing pagination_ellipsis.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
// First page should always be visible.
|
||||||
|
var paginationString = ""
|
||||||
|
if currentPage == 1 {
|
||||||
|
paginationString = strings.Replace(string(paginationLinkCurrentRaw), "{pageNum}", strconv.Itoa(currentPage), -1)
|
||||||
|
} else {
|
||||||
|
paginationString = strings.Replace(string(paginationLinkRaw), "{pageNum}", "1", -1)
|
||||||
|
paginationString = strings.Replace(string(paginationString), "{paginationLink}", linksBase+"1", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ellipsisStartAdded = false
|
||||||
|
var ellipsisEndAdded = false
|
||||||
|
i := 2
|
||||||
|
for i <= pages {
|
||||||
|
if pages > 5 {
|
||||||
|
if currentPage-3 < i && currentPage+3 > i || i == pages {
|
||||||
|
var paginationItemRaw = string(paginationLinkRaw)
|
||||||
|
if i == currentPage {
|
||||||
|
paginationItemRaw = string(paginationLinkCurrentRaw)
|
||||||
|
}
|
||||||
|
paginationItem := strings.Replace(paginationItemRaw, "{pageNum}", strconv.Itoa(i), -1)
|
||||||
|
paginationItem = strings.Replace(paginationItem, "{paginationLink}", linksBase+strconv.Itoa(i), 1)
|
||||||
|
paginationString += paginationItem
|
||||||
|
} else {
|
||||||
|
if currentPage-3 < i && !ellipsisStartAdded {
|
||||||
|
paginationString += string(paginationEllipsisRaw)
|
||||||
|
ellipsisStartAdded = true
|
||||||
|
} else if currentPage+3 > i && !ellipsisEndAdded {
|
||||||
|
paginationString += string(paginationEllipsisRaw)
|
||||||
|
ellipsisEndAdded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var paginationItemRaw = string(paginationLinkRaw)
|
||||||
|
if i == currentPage {
|
||||||
|
paginationItemRaw = string(paginationLinkCurrentRaw)
|
||||||
|
}
|
||||||
|
paginationItem := strings.Replace(paginationItemRaw, "{pageNum}", strconv.Itoa(i), -1)
|
||||||
|
paginationItem = strings.Replace(paginationItem, "{paginationLink}", linksBase+strconv.Itoa(i), 1)
|
||||||
|
paginationString += paginationItem
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pagination := strings.Replace(string(paginationHTMLRaw), "{paginationLinks}", paginationString, 1)
|
||||||
|
if currentPage+1 <= pages {
|
||||||
|
pagination = strings.Replace(pagination, "{nextPageLink}", linksBase+strconv.Itoa(currentPage+1), 1)
|
||||||
|
} else {
|
||||||
|
pagination = strings.Replace(pagination, "{nextPageLink}", linksBase+strconv.Itoa(pages), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentPage-1 > 1 {
|
||||||
|
pagination = strings.Replace(pagination, "{previousPageLink}", linksBase+strconv.Itoa(currentPage-1), 1)
|
||||||
|
} else {
|
||||||
|
pagination = strings.Replace(pagination, "{previousPageLink}", linksBase, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination
|
||||||
|
}
|
146
internal/structs/model.go
Normal file
146
internal/structs/model.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"golang.org/x/crypto/scrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PASTE_KEEP_FOREVER = 0
|
||||||
|
PASTE_KEEP_FOR_MINUTES = 1
|
||||||
|
PASTE_KEEP_FOR_HOURS = 2
|
||||||
|
PASTE_KEEP_FOR_DAYS = 3
|
||||||
|
PASTE_KEEP_FOR_MONTHS = 4
|
||||||
|
|
||||||
|
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PASTE_KEEPS_CORELLATION = map[string]int{
|
||||||
|
"M": PASTE_KEEP_FOR_MINUTES,
|
||||||
|
"h": PASTE_KEEP_FOR_HOURS,
|
||||||
|
"d": PASTE_KEEP_FOR_DAYS,
|
||||||
|
"m": PASTE_KEEP_FOR_MONTHS,
|
||||||
|
"forever": PASTE_KEEP_FOREVER,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Paste represents paste itself.
|
||||||
|
type Paste struct {
|
||||||
|
ID int `db:"id" json:"id"`
|
||||||
|
Title string `db:"title" json:"title"`
|
||||||
|
Data string `db:"data" json:"data"`
|
||||||
|
CreatedAt *time.Time `db:"created_at" json:"created_at"`
|
||||||
|
KeepFor int `db:"keep_for" json:"keep_for"`
|
||||||
|
KeepForUnitType int `db:"keep_for_unit_type" json:"keep_for_unit_type"`
|
||||||
|
Language string `db:"language" json:"language"`
|
||||||
|
Private bool `db:"private" json:"private"`
|
||||||
|
Password string `db:"password" json:"password"`
|
||||||
|
PasswordSalt string `db:"password_salt" json:"password_salt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePassword creates password for current paste.
|
||||||
|
func (p *Paste) CreatePassword(password string) error {
|
||||||
|
// Create salt - random string.
|
||||||
|
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
saltBytes := make([]byte, 64)
|
||||||
|
for i := range saltBytes {
|
||||||
|
saltBytes[i] = charset[seededRand.Intn(len(charset))]
|
||||||
|
}
|
||||||
|
|
||||||
|
saltHashBytes := sha256.Sum256(saltBytes)
|
||||||
|
p.PasswordSalt = fmt.Sprintf("%x", saltHashBytes)
|
||||||
|
|
||||||
|
// Create crypted password and hash it.
|
||||||
|
passwordCrypted, err := scrypt.Key([]byte(password), []byte(p.PasswordSalt), 131072, 8, 1, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
passwordHashBytes := sha256.Sum256(passwordCrypted)
|
||||||
|
p.Password = fmt.Sprintf("%x", passwordHashBytes)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateCryptedCookieValue generates crypted cookie value for paste.
|
||||||
|
func (p *Paste) GenerateCryptedCookieValue() string {
|
||||||
|
cookieValueCrypted, _ := scrypt.Key([]byte(p.Password), []byte(p.PasswordSalt), 131072, 8, 1, 64)
|
||||||
|
return fmt.Sprintf("%x", sha256.Sum256(cookieValueCrypted))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Paste) GetExpirationTime() time.Time {
|
||||||
|
var expirationTime time.Time
|
||||||
|
switch p.KeepForUnitType {
|
||||||
|
case PASTE_KEEP_FOREVER:
|
||||||
|
expirationTime = time.Now().UTC().Add(time.Hour * 1)
|
||||||
|
case PASTE_KEEP_FOR_MINUTES:
|
||||||
|
expirationTime = p.CreatedAt.Add(time.Minute * time.Duration(p.KeepFor))
|
||||||
|
case PASTE_KEEP_FOR_HOURS:
|
||||||
|
expirationTime = p.CreatedAt.Add(time.Hour * time.Duration(p.KeepFor))
|
||||||
|
case PASTE_KEEP_FOR_DAYS:
|
||||||
|
expirationTime = p.CreatedAt.Add(time.Hour * 24 * time.Duration(p.KeepFor))
|
||||||
|
case PASTE_KEEP_FOR_MONTHS:
|
||||||
|
expirationTime = p.CreatedAt.Add(time.Hour * 24 * 30 * time.Duration(p.KeepFor))
|
||||||
|
}
|
||||||
|
|
||||||
|
return expirationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExpired checks if paste is already expired (or not).
|
||||||
|
func (p *Paste) IsExpired() bool {
|
||||||
|
curTime := time.Now().UTC()
|
||||||
|
expirationTime := p.GetExpirationTime()
|
||||||
|
|
||||||
|
if curTime.Sub(expirationTime).Seconds() > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyPassword verifies that provided password is valid.
|
||||||
|
func (p *Paste) VerifyPassword(password string) bool {
|
||||||
|
// Create crypted password and hash it.
|
||||||
|
passwordCrypted, err := scrypt.Key([]byte(password), []byte(p.PasswordSalt), 131072, 8, 1, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
passwordHashBytes := sha256.Sum256(passwordCrypted)
|
||||||
|
providedPassword := fmt.Sprintf("%x", passwordHashBytes)
|
||||||
|
|
||||||
|
if providedPassword == p.Password {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
127
internal/templater/exported.go
Normal file
127
internal/templater/exported.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// Fast Paste Bin - uberfast and easy-to-use pastebin.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
|
||||||
|
// developers.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject
|
||||||
|
// to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package templater
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"gitlab.com/pztrn/fastpastebin/assets/static"
|
||||||
|
"gitlab.com/pztrn/fastpastebin/internal/context"
|
||||||
|
|
||||||
|
// other
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
c *context.Context
|
||||||
|
log zerolog.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetErrorTemplate returns formatted error template.
|
||||||
|
// If error.html wasn't found - it will return "error.html not found"
|
||||||
|
// message as simple string.
|
||||||
|
func GetErrorTemplate(ec echo.Context, errorText string) string {
|
||||||
|
// Getting main error template.
|
||||||
|
mainhtml := GetTemplate(ec, "error.html", map[string]string{"error": errorText})
|
||||||
|
|
||||||
|
return mainhtml
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawTemplate returns only raw template data.
|
||||||
|
func GetRawTemplate(ec echo.Context, templateName string, data map[string]string) string {
|
||||||
|
// Getting main template.
|
||||||
|
tplRaw, err := static.ReadFile(templateName)
|
||||||
|
if err != nil {
|
||||||
|
ec.String(http.StatusBadRequest, templateName+" not found.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
tpl := string(tplRaw)
|
||||||
|
// Replace placeholders with data from data map.
|
||||||
|
for placeholder, value := range data {
|
||||||
|
tpl = strings.Replace(tpl, "{"+placeholder+"}", value, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemplate returns formatted template that can be outputted to client.
|
||||||
|
func GetTemplate(ec echo.Context, name string, data map[string]string) string {
|
||||||
|
log.Debug().Msgf("Requested template '%s'", name)
|
||||||
|
|
||||||
|
// Getting main template.
|
||||||
|
mainhtml, err := static.ReadFile("main.html")
|
||||||
|
if err != nil {
|
||||||
|
ec.String(http.StatusBadRequest, "main.html not found.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting navigation.
|
||||||
|
navhtml, err1 := static.ReadFile("navigation.html")
|
||||||
|
if err1 != nil {
|
||||||
|
ec.String(http.StatusBadRequest, "navigation.html not found.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting footer.
|
||||||
|
footerhtml, err2 := static.ReadFile("footer.html")
|
||||||
|
if err2 != nil {
|
||||||
|
ec.String(http.StatusBadRequest, "footer.html not found.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format main template.
|
||||||
|
tpl := strings.Replace(string(mainhtml), "{navigation}", string(navhtml), 1)
|
||||||
|
tpl = strings.Replace(tpl, "{footer}", string(footerhtml), 1)
|
||||||
|
// Version.
|
||||||
|
tpl = strings.Replace(tpl, "{version}", context.Version, 1)
|
||||||
|
|
||||||
|
// Get requested template.
|
||||||
|
reqhtml, err3 := static.ReadFile(name)
|
||||||
|
if err3 != nil {
|
||||||
|
ec.String(http.StatusBadRequest, name+" not found.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace documentBody.
|
||||||
|
tpl = strings.Replace(tpl, "{documentBody}", string(reqhtml), 1)
|
||||||
|
|
||||||
|
// Replace placeholders with data from data map.
|
||||||
|
for placeholder, value := range data {
|
||||||
|
tpl = strings.Replace(tpl, "{"+placeholder+"}", value, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes package.
|
||||||
|
func Initialize(cc *context.Context) {
|
||||||
|
c = cc
|
||||||
|
log = c.Logger.With().Str("type", "internal").Str("package", "templater").Logger()
|
||||||
|
}
|
56
vendor/github.com/alecthomas/chroma/README.md
generated
vendored
56
vendor/github.com/alecthomas/chroma/README.md
generated
vendored
@ -12,24 +12,49 @@ translators for Pygments lexers and styles.
|
|||||||
|
|
||||||
<!-- MarkdownTOC -->
|
<!-- MarkdownTOC -->
|
||||||
|
|
||||||
- [Supported languages](#supported-languages)
|
1. [Supported languages](#supported-languages)
|
||||||
- [Using the library](#using-the-library)
|
1. [Using the library](#using-the-library)
|
||||||
- [Quick start](#quick-start)
|
1. [Quick start](#quick-start)
|
||||||
- [Identifying the language](#identifying-the-language)
|
1. [Identifying the language](#identifying-the-language)
|
||||||
- [Formatting the output](#formatting-the-output)
|
1. [Formatting the output](#formatting-the-output)
|
||||||
- [The HTML formatter](#the-html-formatter)
|
1. [The HTML formatter](#the-html-formatter)
|
||||||
- [More detail](#more-detail)
|
1. [More detail](#more-detail)
|
||||||
- [Lexers](#lexers)
|
1. [Lexers](#lexers)
|
||||||
- [Formatters](#formatters)
|
1. [Formatters](#formatters)
|
||||||
- [Styles](#styles)
|
1. [Styles](#styles)
|
||||||
- [Command-line interface](#command-line-interface)
|
1. [Command-line interface](#command-line-interface)
|
||||||
- [What's missing compared to Pygments?](#whats-missing-compared-to-pygments)
|
1. [What's missing compared to Pygments?](#whats-missing-compared-to-pygments)
|
||||||
|
|
||||||
<!-- /MarkdownTOC -->
|
<!-- /MarkdownTOC -->
|
||||||
|
|
||||||
## Supported languages
|
## Supported languages
|
||||||
|
|
||||||
ABNF, ANTLR, APL, ActionScript, ActionScript 3, Ada, Angular2, ApacheConf, AppleScript, Awk, BNF, Base Makefile, Bash, Batchfile, BlitzBasic, Brainfuck, C, C#, C++, CFEngine3, CMake, COBOL, CSS, Cap'n Proto, Ceylon, ChaiScript, Cheetah, Clojure, CoffeeScript, Common Lisp, Coq, Crystal, Cython, DTD, Dart, Diff, Django/Jinja, Docker, EBNF, Elixir, Elm, EmacsLisp, Erlang, FSharp, Factor, Fish, Forth, Fortran, GAS, GDScript, GLSL, Genshi, Genshi HTML, Genshi Text, Gnuplot, Go, Groovy, HTML, Handlebars, Haskell, Haxe, Hexdump, Hy, INI, Idris, Io, JSON, Java, JavaScript, Julia, Kotlin, LLVM, Lighttpd configuration file, Lua, Mako, Mason, Mathematica, MiniZinc, Modula-2, MySQL, Myghty, NASM, Newspeak, Nginx configuration file, Nim, OCaml, Octave, PHP, PL/pgSQL, POVRay, PacmanConf, Perl, Pig, PkgConfig, PostScript, PostgreSQL SQL dialect, PowerShell, Prolog, Protocol Buffer, Puppet, Python, Python 3, QBasic, R, Racket, Ragel, Rexx, Ruby, Rust, SPARQL, SQL, Sass, Scala, Scheme, Scilab, Smalltalk, Smarty, Snobol, SquidConf, SVG, Swift, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, Thrift, Transact-SQL, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, VHDL, VimL, XML, Xorg, YAML, cfstatement, markdown, reStructuredText, reg, systemverilog, verilog
|
Prefix | Language
|
||||||
|
:----: | --------
|
||||||
|
A | ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Awk
|
||||||
|
B | Ballerina, Base Makefile, Bash, Batchfile, BlitzBasic, BNF, Brainfuck
|
||||||
|
C | C, C#, C++, Cassandra CQL, CFEngine3, cfstatement/ColdFusion, CMake, COBOL, CSS, Cap'n Proto, Ceylon, ChaiScript, Cheetah, Clojure, CoffeeScript, Common Lisp, Coq, Crystal, Cython
|
||||||
|
D | Dart, Diff, Django/Jinja, Docker, DTD
|
||||||
|
E | EBNF, Elixir, Elm, EmacsLisp, Erlang
|
||||||
|
F | Factor, Fish, Forth, Fortran, FSharp
|
||||||
|
G | GAS, GDScript, GLSL, Genshi, Genshi HTML, Genshi Text, Gnuplot, Go, Go HTML Template, Go Text Template, Groovy
|
||||||
|
H | Handlebars, Haskell, Haxe, Hexdump, HTML, HTTP, Hy
|
||||||
|
I | Idris, INI, Io
|
||||||
|
J | Java, JavaScript, JSON, Jsx, Julia, Jungle
|
||||||
|
K | Kotlin
|
||||||
|
L | Lighttpd configuration file, LLVM, Lua
|
||||||
|
M | Mako, Markdown, Mason, Mathematica, MiniZinc, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL
|
||||||
|
N | NASM, Newspeak, Nginx configuration file, Nim, Nix
|
||||||
|
O | Objective-C, OCaml, Octave, OpenSCAD, Org Mode
|
||||||
|
P | PacmanConf, Perl, PHP, Pig, PkgConfig, Plaintext, PL/pgSQL, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, Protocol Buffer, Puppet, Python, Python 3
|
||||||
|
Q | QBasic
|
||||||
|
R | R, Racket, Ragel, reg, reStructuredText, Rexx, Ruby, Rust
|
||||||
|
S | Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Swift, systemd, Systemverilog
|
||||||
|
T | TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData
|
||||||
|
V | verilog, VHDL, VimL
|
||||||
|
W | WDTE
|
||||||
|
X | XML, Xorg
|
||||||
|
Y | YAML
|
||||||
|
|
||||||
_I will attempt to keep this section up to date, but an authoritative list can be
|
_I will attempt to keep this section up to date, but an authoritative list can be
|
||||||
displayed with `chroma --list`._
|
displayed with `chroma --list`._
|
||||||
@ -133,7 +158,7 @@ err := formatter.Format(w, style, iterator)
|
|||||||
### The HTML formatter
|
### The HTML formatter
|
||||||
|
|
||||||
By default the `html` registered formatter generates standalone HTML with
|
By default the `html` registered formatter generates standalone HTML with
|
||||||
embedded CSS. More flexibility is available through the `lexers/html` package.
|
embedded CSS. More flexibility is available through the `formatters/html` package.
|
||||||
|
|
||||||
Firstly, the output generated by the formatter can be customised with the
|
Firstly, the output generated by the formatter can be customised with the
|
||||||
following constructor options:
|
following constructor options:
|
||||||
@ -144,6 +169,7 @@ following constructor options:
|
|||||||
- `TabWidth(width)` - Set the rendered tab width, in characters.
|
- `TabWidth(width)` - Set the rendered tab width, in characters.
|
||||||
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
|
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
|
||||||
- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
|
- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
|
||||||
|
- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
|
||||||
|
|
||||||
If `WithClasses()` is used, the corresponding CSS can be obtained from the formatter with:
|
If `WithClasses()` is used, the corresponding CSS can be obtained from the formatter with:
|
||||||
|
|
||||||
@ -187,6 +213,8 @@ Chroma styles use the [same syntax](http://pygments.org/docs/styles/) as Pygment
|
|||||||
|
|
||||||
All Pygments styles have been converted to Chroma using the `_tools/style.py` script.
|
All Pygments styles have been converted to Chroma using the `_tools/style.py` script.
|
||||||
|
|
||||||
|
For a quick overview of the available styles and how they look, check out the [Chroma Style Gallery](https://xyproto.github.io/splash/docs/).
|
||||||
|
|
||||||
## Command-line interface
|
## Command-line interface
|
||||||
|
|
||||||
A command-line interface to Chroma is included. It can be installed with:
|
A command-line interface to Chroma is included. It can be installed with:
|
||||||
|
13
vendor/github.com/alecthomas/chroma/coalesce.go
generated
vendored
13
vendor/github.com/alecthomas/chroma/coalesce.go
generated
vendored
@ -6,14 +6,17 @@ func Coalesce(lexer Lexer) Lexer { return &coalescer{lexer} }
|
|||||||
type coalescer struct{ Lexer }
|
type coalescer struct{ Lexer }
|
||||||
|
|
||||||
func (d *coalescer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
|
func (d *coalescer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
|
||||||
var prev *Token
|
var prev Token
|
||||||
it, err := d.Lexer.Tokenise(options, text)
|
it, err := d.Lexer.Tokenise(options, text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return func() *Token {
|
return func() Token {
|
||||||
for token := it(); token != nil; token = it() {
|
for token := it(); token != (EOF); token = it() {
|
||||||
if prev == nil {
|
if len(token.Value) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if prev == EOF {
|
||||||
prev = token
|
prev = token
|
||||||
} else {
|
} else {
|
||||||
if prev.Type == token.Type && len(prev.Value) < 8192 {
|
if prev.Type == token.Type && len(prev.Value) < 8192 {
|
||||||
@ -26,7 +29,7 @@ func (d *coalescer) Tokenise(options *TokeniseOptions, text string) (Iterator, e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out := prev
|
out := prev
|
||||||
prev = nil
|
prev = EOF
|
||||||
return out
|
return out
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
10
vendor/github.com/alecthomas/chroma/colour.go
generated
vendored
10
vendor/github.com/alecthomas/chroma/colour.go
generated
vendored
@ -60,10 +60,12 @@ func NewColour(r, g, b uint8) Colour {
|
|||||||
// This uses the approach described here (https://www.compuphase.com/cmetric.htm).
|
// This uses the approach described here (https://www.compuphase.com/cmetric.htm).
|
||||||
// This is not as accurate as LAB, et. al. but is *vastly* simpler and sufficient for our needs.
|
// This is not as accurate as LAB, et. al. but is *vastly* simpler and sufficient for our needs.
|
||||||
func (c Colour) Distance(e2 Colour) float64 {
|
func (c Colour) Distance(e2 Colour) float64 {
|
||||||
rmean := int(c.Red()+e2.Red()) / 2
|
ar, ag, ab := int64(c.Red()), int64(c.Green()), int64(c.Blue())
|
||||||
r := int(c.Red() - e2.Red())
|
br, bg, bb := int64(e2.Red()), int64(e2.Green()), int64(e2.Blue())
|
||||||
g := int(c.Green() - e2.Green())
|
rmean := (ar + br) / 2
|
||||||
b := int(c.Blue() - e2.Blue())
|
r := ar - br
|
||||||
|
g := ag - bg
|
||||||
|
b := ab - bb
|
||||||
return math.Sqrt(float64((((512 + rmean) * r * r) >> 8) + 4*g*g + (((767 - rmean) * b * b) >> 8)))
|
return math.Sqrt(float64((((512 + rmean) * r * r) >> 8) + 4*g*g + (((767 - rmean) * b * b) >> 8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
137
vendor/github.com/alecthomas/chroma/delegate.go
generated
vendored
Normal file
137
vendor/github.com/alecthomas/chroma/delegate.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package chroma
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type delegatingLexer struct {
|
||||||
|
root Lexer
|
||||||
|
language Lexer
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelegatingLexer combines two lexers to handle the common case of a language embedded inside another, such as PHP
|
||||||
|
// inside HTML or PHP inside plain text.
|
||||||
|
//
|
||||||
|
// It takes two lexer as arguments: a root lexer and a language lexer. First everything is scanned using the language
|
||||||
|
// lexer, which must return "Other" for unrecognised tokens. Then all "Other" tokens are lexed using the root lexer.
|
||||||
|
// Finally, these two sets of tokens are merged.
|
||||||
|
//
|
||||||
|
// The lexers from the template lexer package use this base lexer.
|
||||||
|
func DelegatingLexer(root Lexer, language Lexer) Lexer {
|
||||||
|
return &delegatingLexer{
|
||||||
|
root: root,
|
||||||
|
language: language,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *delegatingLexer) Config() *Config {
|
||||||
|
return d.language.Config()
|
||||||
|
}
|
||||||
|
|
||||||
|
// An insertion is the character range where language tokens should be inserted.
|
||||||
|
type insertion struct {
|
||||||
|
start, end int
|
||||||
|
tokens []Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *delegatingLexer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
|
||||||
|
tokens, err := Tokenise(Coalesce(d.language), options, text)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Compute insertions and gather "Other" tokens.
|
||||||
|
others := &bytes.Buffer{}
|
||||||
|
insertions := []*insertion{}
|
||||||
|
var insert *insertion
|
||||||
|
offset := 0
|
||||||
|
var last Token
|
||||||
|
for _, t := range tokens {
|
||||||
|
if t.Type == Other {
|
||||||
|
if last != EOF && insert != nil && last.Type != Other {
|
||||||
|
insert.end = offset
|
||||||
|
}
|
||||||
|
others.WriteString(t.Value)
|
||||||
|
} else {
|
||||||
|
if last == EOF || last.Type == Other {
|
||||||
|
insert = &insertion{start: offset}
|
||||||
|
insertions = append(insertions, insert)
|
||||||
|
}
|
||||||
|
insert.tokens = append(insert.tokens, t)
|
||||||
|
}
|
||||||
|
last = t
|
||||||
|
offset += len(t.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(insertions) == 0 {
|
||||||
|
return d.root.Tokenise(options, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lex the other tokens.
|
||||||
|
rootTokens, err := Tokenise(Coalesce(d.root), options, others.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interleave the two sets of tokens.
|
||||||
|
var out []Token
|
||||||
|
offset = 0 // Offset into text.
|
||||||
|
tokenIndex := 0
|
||||||
|
nextToken := func() Token {
|
||||||
|
if tokenIndex >= len(rootTokens) {
|
||||||
|
return EOF
|
||||||
|
}
|
||||||
|
t := rootTokens[tokenIndex]
|
||||||
|
tokenIndex++
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
insertionIndex := 0
|
||||||
|
nextInsertion := func() *insertion {
|
||||||
|
if insertionIndex >= len(insertions) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
i := insertions[insertionIndex]
|
||||||
|
insertionIndex++
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
t := nextToken()
|
||||||
|
i := nextInsertion()
|
||||||
|
for t != EOF || i != nil {
|
||||||
|
// fmt.Printf("%d->%d:%q %d->%d:%q\n", offset, offset+len(t.Value), t.Value, i.start, i.end, Stringify(i.tokens...))
|
||||||
|
if t == EOF || (i != nil && i.start < offset+len(t.Value)) {
|
||||||
|
var l Token
|
||||||
|
l, t = splitToken(t, i.start-offset)
|
||||||
|
if l != EOF {
|
||||||
|
out = append(out, l)
|
||||||
|
offset += len(l.Value)
|
||||||
|
}
|
||||||
|
out = append(out, i.tokens...)
|
||||||
|
offset += i.end - i.start
|
||||||
|
if t == EOF {
|
||||||
|
t = nextToken()
|
||||||
|
}
|
||||||
|
i = nextInsertion()
|
||||||
|
} else {
|
||||||
|
out = append(out, t)
|
||||||
|
offset += len(t.Value)
|
||||||
|
t = nextToken()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Literator(out...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitToken(t Token, offset int) (l Token, r Token) {
|
||||||
|
if t == EOF {
|
||||||
|
return EOF, EOF
|
||||||
|
}
|
||||||
|
if offset == 0 {
|
||||||
|
return EOF, t
|
||||||
|
}
|
||||||
|
if offset == len(t.Value) {
|
||||||
|
return t, EOF
|
||||||
|
}
|
||||||
|
l = t.Clone()
|
||||||
|
r = t.Clone()
|
||||||
|
l.Value = l.Value[:offset]
|
||||||
|
r.Value = r.Value[offset:]
|
||||||
|
return
|
||||||
|
}
|
2
vendor/github.com/alecthomas/chroma/formatters/api.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/formatters/api.go
generated
vendored
@ -11,7 +11,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
// NoOp formatter.
|
// NoOp formatter.
|
||||||
NoOp = Register("noop", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, iterator chroma.Iterator) error {
|
NoOp = Register("noop", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, iterator chroma.Iterator) error {
|
||||||
for t := iterator(); t != nil; t = iterator() {
|
for t := iterator(); t != chroma.EOF; t = iterator() {
|
||||||
if _, err := io.WriteString(w, t.Value); err != nil {
|
if _, err := io.WriteString(w, t.Value); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
67
vendor/github.com/alecthomas/chroma/formatters/html/html.go
generated
vendored
67
vendor/github.com/alecthomas/chroma/formatters/html/html.go
generated
vendored
@ -25,6 +25,9 @@ func WithClasses() Option { return func(f *Formatter) { f.Classes = true } }
|
|||||||
// TabWidth sets the number of characters for a tab. Defaults to 8.
|
// TabWidth sets the number of characters for a tab. Defaults to 8.
|
||||||
func TabWidth(width int) Option { return func(f *Formatter) { f.tabWidth = width } }
|
func TabWidth(width int) Option { return func(f *Formatter) { f.tabWidth = width } }
|
||||||
|
|
||||||
|
// PreventSurroundingPre prevents the surrounding pre tags around the generated code
|
||||||
|
func PreventSurroundingPre() Option { return func(f *Formatter) { f.preventSurroundingPre = true } }
|
||||||
|
|
||||||
// WithLineNumbers formats output with line numbers.
|
// WithLineNumbers formats output with line numbers.
|
||||||
func WithLineNumbers() Option {
|
func WithLineNumbers() Option {
|
||||||
return func(f *Formatter) {
|
return func(f *Formatter) {
|
||||||
@ -70,14 +73,15 @@ func New(options ...Option) *Formatter {
|
|||||||
|
|
||||||
// Formatter that generates HTML.
|
// Formatter that generates HTML.
|
||||||
type Formatter struct {
|
type Formatter struct {
|
||||||
standalone bool
|
standalone bool
|
||||||
prefix string
|
prefix string
|
||||||
Classes bool // Exported field to detect when classes are being used
|
Classes bool // Exported field to detect when classes are being used
|
||||||
tabWidth int
|
preventSurroundingPre bool
|
||||||
lineNumbers bool
|
tabWidth int
|
||||||
lineNumbersInTable bool
|
lineNumbers bool
|
||||||
highlightRanges highlightRanges
|
lineNumbersInTable bool
|
||||||
baseLineNumber int
|
highlightRanges highlightRanges
|
||||||
|
baseLineNumber int
|
||||||
}
|
}
|
||||||
|
|
||||||
type highlightRanges [][2]int
|
type highlightRanges [][2]int
|
||||||
@ -125,7 +129,7 @@ func (f *Formatter) restyle(style *chroma.Style) (*chroma.Style, error) {
|
|||||||
// We deliberately don't use html/template here because it is two orders of magnitude slower (benchmarked).
|
// We deliberately don't use html/template here because it is two orders of magnitude slower (benchmarked).
|
||||||
//
|
//
|
||||||
// OTOH we need to be super careful about correct escaping...
|
// OTOH we need to be super careful about correct escaping...
|
||||||
func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma.Token) (err error) { // nolint: gocyclo
|
func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.Token) (err error) { // nolint: gocyclo
|
||||||
style, err = f.restyle(style)
|
style, err = f.restyle(style)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -149,7 +153,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma
|
|||||||
|
|
||||||
wrapInTable := f.lineNumbers && f.lineNumbersInTable
|
wrapInTable := f.lineNumbers && f.lineNumbersInTable
|
||||||
|
|
||||||
lines := splitTokensIntoLines(tokens)
|
lines := chroma.SplitTokensIntoLines(tokens)
|
||||||
lineDigits := len(fmt.Sprintf("%d", len(lines)))
|
lineDigits := len(fmt.Sprintf("%d", len(lines)))
|
||||||
highlightIndex := 0
|
highlightIndex := 0
|
||||||
|
|
||||||
@ -158,7 +162,9 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma
|
|||||||
fmt.Fprintf(w, "<div%s>\n", f.styleAttr(css, chroma.Background))
|
fmt.Fprintf(w, "<div%s>\n", f.styleAttr(css, chroma.Background))
|
||||||
fmt.Fprintf(w, "<table%s><tr>", f.styleAttr(css, chroma.LineTable))
|
fmt.Fprintf(w, "<table%s><tr>", f.styleAttr(css, chroma.LineTable))
|
||||||
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
|
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
|
||||||
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
|
if !f.preventSurroundingPre {
|
||||||
|
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
|
||||||
|
}
|
||||||
for index := range lines {
|
for index := range lines {
|
||||||
line := f.baseLineNumber + index
|
line := f.baseLineNumber + index
|
||||||
highlight, next := f.shouldHighlight(highlightIndex, line)
|
highlight, next := f.shouldHighlight(highlightIndex, line)
|
||||||
@ -175,11 +181,16 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma
|
|||||||
fmt.Fprintf(w, "</span>")
|
fmt.Fprintf(w, "</span>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, "</pre></td>\n")
|
if !f.preventSurroundingPre {
|
||||||
|
fmt.Fprint(w, "</pre>")
|
||||||
|
}
|
||||||
|
fmt.Fprint(w, "</td>\n")
|
||||||
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
|
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
|
if !f.preventSurroundingPre {
|
||||||
|
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
|
||||||
|
}
|
||||||
highlightIndex = 0
|
highlightIndex = 0
|
||||||
for index, tokens := range lines {
|
for index, tokens := range lines {
|
||||||
// 1-based line number.
|
// 1-based line number.
|
||||||
@ -209,7 +220,9 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []*chroma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprint(w, "</pre>")
|
if !f.preventSurroundingPre {
|
||||||
|
fmt.Fprint(w, "</pre>")
|
||||||
|
}
|
||||||
|
|
||||||
if wrapInTable {
|
if wrapInTable {
|
||||||
fmt.Fprint(w, "</td></tr></table>\n")
|
fmt.Fprint(w, "</td></tr></table>\n")
|
||||||
@ -354,6 +367,9 @@ func StyleEntryToCSS(e chroma.StyleEntry) string {
|
|||||||
if e.Italic == chroma.Yes {
|
if e.Italic == chroma.Yes {
|
||||||
styles = append(styles, "font-style: italic")
|
styles = append(styles, "font-style: italic")
|
||||||
}
|
}
|
||||||
|
if e.Underline == chroma.Yes {
|
||||||
|
styles = append(styles, "text-decoration: underline")
|
||||||
|
}
|
||||||
return strings.Join(styles, "; ")
|
return strings.Join(styles, "; ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,26 +390,3 @@ func compressStyle(s string) string {
|
|||||||
}
|
}
|
||||||
return strings.Join(out, ";")
|
return strings.Join(out, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitTokensIntoLines(tokens []*chroma.Token) (out [][]*chroma.Token) {
|
|
||||||
line := []*chroma.Token{}
|
|
||||||
for _, token := range tokens {
|
|
||||||
for strings.Contains(token.Value, "\n") {
|
|
||||||
parts := strings.SplitAfterN(token.Value, "\n", 2)
|
|
||||||
// Token becomes the tail.
|
|
||||||
token.Value = parts[1]
|
|
||||||
|
|
||||||
// Append the head to the line and flush the line.
|
|
||||||
clone := token.Clone()
|
|
||||||
clone.Value = parts[0]
|
|
||||||
line = append(line, clone)
|
|
||||||
out = append(out, line)
|
|
||||||
line = nil
|
|
||||||
}
|
|
||||||
line = append(line, token)
|
|
||||||
}
|
|
||||||
if len(line) > 0 {
|
|
||||||
out = append(out, line)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
2
vendor/github.com/alecthomas/chroma/formatters/json.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/formatters/json.go
generated
vendored
@ -12,7 +12,7 @@ import (
|
|||||||
var JSON = Register("json", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
var JSON = Register("json", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
||||||
fmt.Fprintln(w, "[")
|
fmt.Fprintln(w, "[")
|
||||||
i := 0
|
i := 0
|
||||||
for t := it(); t != nil; t = it() {
|
for t := it(); t != chroma.EOF; t = it() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
fmt.Fprintln(w, ",")
|
fmt.Fprintln(w, ",")
|
||||||
}
|
}
|
||||||
|
2
vendor/github.com/alecthomas/chroma/formatters/tokens.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/formatters/tokens.go
generated
vendored
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// Tokens formatter outputs the raw token structures.
|
// Tokens formatter outputs the raw token structures.
|
||||||
var Tokens = Register("tokens", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
var Tokens = Register("tokens", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
||||||
for t := it(); t != nil; t = it() {
|
for t := it(); t != chroma.EOF; t = it() {
|
||||||
if _, err := fmt.Fprintln(w, t.GoString()); err != nil {
|
if _, err := fmt.Fprintln(w, t.GoString()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
2
vendor/github.com/alecthomas/chroma/formatters/tty_indexed.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/formatters/tty_indexed.go
generated
vendored
@ -216,7 +216,7 @@ func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
theme := styleToEscapeSequence(c.table, style)
|
theme := styleToEscapeSequence(c.table, style)
|
||||||
for token := it(); token != nil; token = it() {
|
for token := it(); token != chroma.EOF; token = it() {
|
||||||
// TODO: Cache token lookups?
|
// TODO: Cache token lookups?
|
||||||
clr, ok := theme[token.Type]
|
clr, ok := theme[token.Type]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
2
vendor/github.com/alecthomas/chroma/formatters/tty_truecolour.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/formatters/tty_truecolour.go
generated
vendored
@ -11,7 +11,7 @@ import (
|
|||||||
var TTY16m = Register("terminal16m", chroma.FormatterFunc(trueColourFormatter))
|
var TTY16m = Register("terminal16m", chroma.FormatterFunc(trueColourFormatter))
|
||||||
|
|
||||||
func trueColourFormatter(w io.Writer, style *chroma.Style, it chroma.Iterator) error {
|
func trueColourFormatter(w io.Writer, style *chroma.Style, it chroma.Iterator) error {
|
||||||
for token := it(); token != nil; token = it() {
|
for token := it(); token != chroma.EOF; token = it() {
|
||||||
entry := style.Get(token.Type)
|
entry := style.Get(token.Type)
|
||||||
if !entry.IsZero() {
|
if !entry.IsZero() {
|
||||||
out := ""
|
out := ""
|
||||||
|
14
vendor/github.com/alecthomas/chroma/go.mod
generated
vendored
Normal file
14
vendor/github.com/alecthomas/chroma/go.mod
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module github.com/alecthomas/chroma
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38
|
||||||
|
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 // indirect
|
||||||
|
github.com/alecthomas/kong v0.1.15
|
||||||
|
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 // indirect
|
||||||
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
|
||||||
|
github.com/dlclark/regexp2 v1.1.6
|
||||||
|
github.com/mattn/go-colorable v0.0.9
|
||||||
|
github.com/mattn/go-isatty v0.0.4
|
||||||
|
github.com/sergi/go-diff v1.0.0 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect
|
||||||
|
)
|
26
vendor/github.com/alecthomas/chroma/go.sum
generated
vendored
Normal file
26
vendor/github.com/alecthomas/chroma/go.sum
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||||
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
||||||
|
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
|
||||||
|
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
|
||||||
|
github.com/alecthomas/kong v0.1.15 h1:IWBg+KrLvoHBicD50OzMI8fKjrtAa1okMR9g38HVM/s=
|
||||||
|
github.com/alecthomas/kong v0.1.15/go.mod h1:0m2VYms8rH0qbCqVB2gvGHk74bqLIq0HXjCs5bNbNQU=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
||||||
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
|
||||||
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
|
||||||
|
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/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
|
||||||
|
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
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/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
52
vendor/github.com/alecthomas/chroma/iterator.go
generated
vendored
52
vendor/github.com/alecthomas/chroma/iterator.go
generated
vendored
@ -1,16 +1,18 @@
|
|||||||
package chroma
|
package chroma
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// An Iterator across tokens.
|
// An Iterator across tokens.
|
||||||
//
|
//
|
||||||
// nil will be returned at the end of the Token stream.
|
// nil will be returned at the end of the Token stream.
|
||||||
//
|
//
|
||||||
// If an error occurs within an Iterator, it may propagate this in a panic. Formatters should recover.
|
// If an error occurs within an Iterator, it may propagate this in a panic. Formatters should recover.
|
||||||
type Iterator func() *Token
|
type Iterator func() Token
|
||||||
|
|
||||||
// Tokens consumes all tokens from the iterator and returns them as a slice.
|
// Tokens consumes all tokens from the iterator and returns them as a slice.
|
||||||
func (i Iterator) Tokens() []*Token {
|
func (i Iterator) Tokens() []Token {
|
||||||
out := []*Token{}
|
var out []Token
|
||||||
for t := i(); t != nil; t = i() {
|
for t := i(); t != EOF; t = i() {
|
||||||
out = append(out, t)
|
out = append(out, t)
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
@ -18,26 +20,56 @@ func (i Iterator) Tokens() []*Token {
|
|||||||
|
|
||||||
// Concaterator concatenates tokens from a series of iterators.
|
// Concaterator concatenates tokens from a series of iterators.
|
||||||
func Concaterator(iterators ...Iterator) Iterator {
|
func Concaterator(iterators ...Iterator) Iterator {
|
||||||
return func() *Token {
|
return func() Token {
|
||||||
for len(iterators) > 0 {
|
for len(iterators) > 0 {
|
||||||
t := iterators[0]()
|
t := iterators[0]()
|
||||||
if t != nil {
|
if t != EOF {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
iterators = iterators[1:]
|
iterators = iterators[1:]
|
||||||
}
|
}
|
||||||
return nil
|
return EOF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Literator converts a sequence of literal Tokens into an Iterator.
|
// Literator converts a sequence of literal Tokens into an Iterator.
|
||||||
func Literator(tokens ...*Token) Iterator {
|
func Literator(tokens ...Token) Iterator {
|
||||||
return func() (out *Token) {
|
return func() Token {
|
||||||
if len(tokens) == 0 {
|
if len(tokens) == 0 {
|
||||||
return nil
|
return EOF
|
||||||
}
|
}
|
||||||
token := tokens[0]
|
token := tokens[0]
|
||||||
tokens = tokens[1:]
|
tokens = tokens[1:]
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SplitTokensIntoLines(tokens []Token) (out [][]Token) {
|
||||||
|
var line []Token
|
||||||
|
for _, token := range tokens {
|
||||||
|
for strings.Contains(token.Value, "\n") {
|
||||||
|
parts := strings.SplitAfterN(token.Value, "\n", 2)
|
||||||
|
// Token becomes the tail.
|
||||||
|
token.Value = parts[1]
|
||||||
|
|
||||||
|
// Append the head to the line and flush the line.
|
||||||
|
clone := token.Clone()
|
||||||
|
clone.Value = parts[0]
|
||||||
|
line = append(line, clone)
|
||||||
|
out = append(out, line)
|
||||||
|
line = nil
|
||||||
|
}
|
||||||
|
line = append(line, token)
|
||||||
|
}
|
||||||
|
if len(line) > 0 {
|
||||||
|
out = append(out, line)
|
||||||
|
}
|
||||||
|
// Strip empty trailing token line.
|
||||||
|
if len(out) > 0 {
|
||||||
|
last := out[len(out)-1]
|
||||||
|
if len(last) == 1 && last[0].Value == "" {
|
||||||
|
out = out[:len(out)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
10
vendor/github.com/alecthomas/chroma/lexer.go
generated
vendored
10
vendor/github.com/alecthomas/chroma/lexer.go
generated
vendored
@ -64,14 +64,14 @@ type Token struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) String() string { return t.Value }
|
func (t *Token) String() string { return t.Value }
|
||||||
func (t *Token) GoString() string { return fmt.Sprintf("Token{%s, %q}", t.Type, t.Value) }
|
func (t *Token) GoString() string { return fmt.Sprintf("&Token{%s, %q}", t.Type, t.Value) }
|
||||||
|
|
||||||
func (t *Token) Clone() *Token {
|
func (t *Token) Clone() Token {
|
||||||
clone := &Token{}
|
return *t
|
||||||
*clone = *t
|
|
||||||
return clone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var EOF Token
|
||||||
|
|
||||||
type TokeniseOptions struct {
|
type TokeniseOptions struct {
|
||||||
// State to start tokenisation in. Defaults to "root".
|
// State to start tokenisation in. Defaults to "root".
|
||||||
State string
|
State string
|
||||||
|
46
vendor/github.com/alecthomas/chroma/lexers/b/ballerina.go
generated
vendored
Normal file
46
vendor/github.com/alecthomas/chroma/lexers/b/ballerina.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package b
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ballerina lexer.
|
||||||
|
var Ballerina = internal.Register(MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "Ballerina",
|
||||||
|
Aliases: []string{"ballerina"},
|
||||||
|
Filenames: []string{"*.bal"},
|
||||||
|
MimeTypes: []string{"text/x-ballerina"},
|
||||||
|
DotAll: true,
|
||||||
|
},
|
||||||
|
Rules{
|
||||||
|
"root": {
|
||||||
|
{`[^\S\n]+`, Text, nil},
|
||||||
|
{`//.*?\n`, CommentSingle, nil},
|
||||||
|
{`/\*.*?\*/`, CommentMultiline, nil},
|
||||||
|
{`(break|catch|continue|done|else|finally|foreach|forever|fork|if|lock|match|return|throw|transaction|try|while)\b`, Keyword, nil},
|
||||||
|
{`((?:(?:[^\W\d]|\$)[\w.\[\]$<>]*\s+)+?)((?:[^\W\d]|\$)[\w$]*)(\s*)(\()`, ByGroups(UsingSelf("root"), NameFunction, Text, Operator), nil},
|
||||||
|
{`@[^\W\d][\w.]*`, NameDecorator, nil},
|
||||||
|
{`(annotation|bind|but|endpoint|error|function|object|private|public|returns|service|type|var|with|worker)\b`, KeywordDeclaration, nil},
|
||||||
|
{`(boolean|byte|decimal|float|int|json|map|nil|record|string|table|xml)\b`, KeywordType, nil},
|
||||||
|
{`(true|false|null)\b`, KeywordConstant, nil},
|
||||||
|
{`import(\s+)`, ByGroups(KeywordNamespace, Text), Push("import")},
|
||||||
|
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
|
||||||
|
{`'\\.'|'[^\\]'|'\\u[0-9a-fA-F]{4}'`, LiteralStringChar, nil},
|
||||||
|
{`(\.)((?:[^\W\d]|\$)[\w$]*)`, ByGroups(Operator, NameAttribute), nil},
|
||||||
|
{`^\s*([^\W\d]|\$)[\w$]*:`, NameLabel, nil},
|
||||||
|
{`([^\W\d]|\$)[\w$]*`, Name, nil},
|
||||||
|
{`([0-9][0-9_]*\.([0-9][0-9_]*)?|\.[0-9][0-9_]*)([eE][+\-]?[0-9][0-9_]*)?[fFdD]?|[0-9][eE][+\-]?[0-9][0-9_]*[fFdD]?|[0-9]([eE][+\-]?[0-9][0-9_]*)?[fFdD]|0[xX]([0-9a-fA-F][0-9a-fA-F_]*\.?|([0-9a-fA-F][0-9a-fA-F_]*)?\.[0-9a-fA-F][0-9a-fA-F_]*)[pP][+\-]?[0-9][0-9_]*[fFdD]?`, LiteralNumberFloat, nil},
|
||||||
|
{`0[xX][0-9a-fA-F][0-9a-fA-F_]*[lL]?`, LiteralNumberHex, nil},
|
||||||
|
{`0[bB][01][01_]*[lL]?`, LiteralNumberBin, nil},
|
||||||
|
{`0[0-7_]+[lL]?`, LiteralNumberOct, nil},
|
||||||
|
{`0|[1-9][0-9_]*[lL]?`, LiteralNumberInteger, nil},
|
||||||
|
{`[~^*!%&\[\](){}<>|+=:;,./?-]`, Operator, nil},
|
||||||
|
{`\n`, Text, nil},
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
{`[\w.]+`, NameNamespace, Pop(1)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
2
vendor/github.com/alecthomas/chroma/lexers/b/bash.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/lexers/b/bash.go
generated
vendored
@ -36,7 +36,7 @@ var Bash = internal.Register(MustNewLexer(
|
|||||||
{`\b(if|fi|else|while|do|done|for|then|return|function|case|select|continue|until|esac|elif)(\s*)\b`, ByGroups(Keyword, Text), nil},
|
{`\b(if|fi|else|while|do|done|for|then|return|function|case|select|continue|until|esac|elif)(\s*)\b`, ByGroups(Keyword, Text), nil},
|
||||||
{"\\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|complete|declare|dirs|disown|echo|enable|eval|exec|exit|export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|shopt|source|suspend|test|time|times|trap|true|type|typeset|ulimit|umask|unalias|unset|wait)(?=[\\s)`])", NameBuiltin, nil},
|
{"\\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|complete|declare|dirs|disown|echo|enable|eval|exec|exit|export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|shopt|source|suspend|test|time|times|trap|true|type|typeset|ulimit|umask|unalias|unset|wait)(?=[\\s)`])", NameBuiltin, nil},
|
||||||
{`\A#!.+\n`, CommentPreproc, nil},
|
{`\A#!.+\n`, CommentPreproc, nil},
|
||||||
{`#.*\n`, CommentSingle, nil},
|
{`#.*\S`, CommentSingle, nil},
|
||||||
{`\\[\w\W]`, LiteralStringEscape, nil},
|
{`\\[\w\W]`, LiteralStringEscape, nil},
|
||||||
{`(\b\w+)(\s*)(\+?=)`, ByGroups(NameVariable, Text, Operator), nil},
|
{`(\b\w+)(\s*)(\+?=)`, ByGroups(NameVariable, Text, Operator), nil},
|
||||||
{`[\[\]{}()=]`, Operator, nil},
|
{`[\[\]{}()=]`, Operator, nil},
|
||||||
|
2
vendor/github.com/alecthomas/chroma/lexers/c/cpp.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/lexers/c/cpp.go
generated
vendored
@ -18,7 +18,7 @@ var CPP = internal.Register(MustNewLexer(
|
|||||||
"statements": {
|
"statements": {
|
||||||
{Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`), Keyword, nil},
|
{Words(``, `\b`, `catch`, `const_cast`, `delete`, `dynamic_cast`, `explicit`, `export`, `friend`, `mutable`, `namespace`, `new`, `operator`, `private`, `protected`, `public`, `reinterpret_cast`, `restrict`, `static_cast`, `template`, `this`, `throw`, `throws`, `try`, `typeid`, `typename`, `using`, `virtual`, `constexpr`, `nullptr`, `decltype`, `thread_local`, `alignas`, `alignof`, `static_assert`, `noexcept`, `override`, `final`), Keyword, nil},
|
||||||
{`char(16_t|32_t)\b`, KeywordType, nil},
|
{`char(16_t|32_t)\b`, KeywordType, nil},
|
||||||
{`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")},
|
{`(class)\b`, ByGroups(Keyword, Text), Push("classname")},
|
||||||
{`(R)(")([^\\()\s]{,16})(\()((?:.|\n)*?)(\)\3)(")`, ByGroups(LiteralStringAffix, LiteralString, LiteralStringDelimiter, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, LiteralString), nil},
|
{`(R)(")([^\\()\s]{,16})(\()((?:.|\n)*?)(\)\3)(")`, ByGroups(LiteralStringAffix, LiteralString, LiteralStringDelimiter, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, LiteralString), nil},
|
||||||
{`(u8|u|U)(")`, ByGroups(LiteralStringAffix, LiteralString), Push("string")},
|
{`(u8|u|U)(")`, ByGroups(LiteralStringAffix, LiteralString), Push("string")},
|
||||||
{`(L?)(")`, ByGroups(LiteralStringAffix, LiteralString), Push("string")},
|
{`(L?)(")`, ByGroups(LiteralStringAffix, LiteralString), Push("string")},
|
||||||
|
69
vendor/github.com/alecthomas/chroma/lexers/c/cql.go
generated
vendored
Normal file
69
vendor/github.com/alecthomas/chroma/lexers/c/cql.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package c
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CassandraCQL lexer.
|
||||||
|
var CassandraCQL = internal.Register(MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "Cassandra CQL",
|
||||||
|
Aliases: []string{"cassandra", "cql"},
|
||||||
|
Filenames: []string{"*.cql"},
|
||||||
|
MimeTypes: []string{"text/x-cql"},
|
||||||
|
NotMultiline: true,
|
||||||
|
CaseInsensitive: true,
|
||||||
|
},
|
||||||
|
Rules{
|
||||||
|
"root": {
|
||||||
|
{`\s+`, TextWhitespace, nil},
|
||||||
|
{`(--|\/\/).*\n?`, CommentSingle, nil},
|
||||||
|
{`/\*`, CommentMultiline, Push("multiline-comments")},
|
||||||
|
{`(ascii|bigint|blob|boolean|counter|date|decimal|double|float|frozen|inet|int|list|map|set|smallint|text|time|timestamp|timeuuid|tinyint|tuple|uuid|varchar|varint)\b`, NameBuiltin, nil},
|
||||||
|
{Words(``, `\b`, `ADD`, `AGGREGATE`, `ALL`, `ALLOW`, `ALTER`, `AND`, `ANY`, `APPLY`, `AS`, `ASC`, `AUTHORIZE`, `BATCH`, `BEGIN`, `BY`, `CLUSTERING`, `COLUMNFAMILY`, `COMPACT`, `CONSISTENCY`, `COUNT`, `CREATE`, `CUSTOM`, `DELETE`, `DESC`, `DISTINCT`, `DROP`, `EACH_QUORUM`, `ENTRIES`, `EXISTS`, `FILTERING`, `FROM`, `FULL`, `GRANT`, `IF`, `IN`, `INDEX`, `INFINITY`, `INSERT`, `INTO`, `KEY`, `KEYS`, `KEYSPACE`, `KEYSPACES`, `LEVEL`, `LIMIT`, `LOCAL_ONE`, `LOCAL_QUORUM`, `MATERIALIZED`, `MODIFY`, `NAN`, `NORECURSIVE`, `NOSUPERUSER`, `NOT`, `OF`, `ON`, `ONE`, `ORDER`, `PARTITION`, `PASSWORD`, `PER`, `PERMISSION`, `PERMISSIONS`, `PRIMARY`, `QUORUM`, `RENAME`, `REVOKE`, `SCHEMA`, `SELECT`, `STATIC`, `STORAGE`, `SUPERUSER`, `TABLE`, `THREE`, `TO`, `TOKEN`, `TRUNCATE`, `TTL`, `TWO`, `TYPE`, `UNLOGGED`, `UPDATE`, `USE`, `USER`, `USERS`, `USING`, `VALUES`, `VIEW`, `WHERE`, `WITH`, `WRITETIME`, `REPLICATION`, `OR`, `REPLACE`, `FUNCTION`, `CALLED`, `INPUT`, `RETURNS`, `LANGUAGE`, `ROLE`, `ROLES`, `TRIGGER`, `DURABLE_WRITES`, `LOGIN`, `OPTIONS`, `LOGGED`, `SFUNC`, `STYPE`, `FINALFUNC`, `INITCOND`, `IS`, `CONTAINS`, `JSON`, `PAGING`, `OFF`), Keyword, nil},
|
||||||
|
{"[+*/<>=~!@#%^&|`?-]+", Operator, nil},
|
||||||
|
{`(?s)(java|javascript)(\s+)(AS)(\s+)('|\$\$)(.*?)(\5)`,
|
||||||
|
UsingByGroup(
|
||||||
|
internal.Get,
|
||||||
|
1, 6,
|
||||||
|
NameBuiltin, TextWhitespace, Keyword, TextWhitespace,
|
||||||
|
LiteralStringHeredoc, LiteralStringHeredoc, LiteralStringHeredoc,
|
||||||
|
),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{`(true|false|null)\b`, KeywordConstant, nil},
|
||||||
|
{`0x[0-9a-f]+`, LiteralNumberHex, nil},
|
||||||
|
{`[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`, LiteralNumberHex, nil},
|
||||||
|
{`\.[0-9]+(e[+-]?[0-9]+)?`, Error, nil},
|
||||||
|
{`-?[0-9]+(\.[0-9])?(e[+-]?[0-9]+)?`, LiteralNumberFloat, nil},
|
||||||
|
{`[0-9]+`, LiteralNumberInteger, nil},
|
||||||
|
{`'`, LiteralStringSingle, Push("string")},
|
||||||
|
{`"`, LiteralStringName, Push("quoted-ident")},
|
||||||
|
{`\$\$`, LiteralStringHeredoc, Push("dollar-string")},
|
||||||
|
{`[a-z_]\w*`, Name, nil},
|
||||||
|
{`:(['"]?)[a-z]\w*\b\1`, NameVariable, nil},
|
||||||
|
{`[;:()\[\]\{\},.]`, Punctuation, nil},
|
||||||
|
},
|
||||||
|
"multiline-comments": {
|
||||||
|
{`/\*`, CommentMultiline, Push("multiline-comments")},
|
||||||
|
{`\*/`, CommentMultiline, Pop(1)},
|
||||||
|
{`[^/*]+`, CommentMultiline, nil},
|
||||||
|
{`[/*]`, CommentMultiline, nil},
|
||||||
|
},
|
||||||
|
"string": {
|
||||||
|
{`[^']+`, LiteralStringSingle, nil},
|
||||||
|
{`''`, LiteralStringSingle, nil},
|
||||||
|
{`'`, LiteralStringSingle, Pop(1)},
|
||||||
|
},
|
||||||
|
"quoted-ident": {
|
||||||
|
{`[^"]+`, LiteralStringName, nil},
|
||||||
|
{`""`, LiteralStringName, nil},
|
||||||
|
{`"`, LiteralStringName, Pop(1)},
|
||||||
|
},
|
||||||
|
"dollar-string": {
|
||||||
|
{`[^\$]+`, LiteralStringHeredoc, nil},
|
||||||
|
{`\$\$`, LiteralStringHeredoc, Pop(1)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
1
vendor/github.com/alecthomas/chroma/lexers/c/csharp.go
generated
vendored
1
vendor/github.com/alecthomas/chroma/lexers/c/csharp.go
generated
vendored
@ -35,7 +35,6 @@ var CSharp = internal.Register(MustNewLexer(
|
|||||||
{`(global)(::)`, ByGroups(Keyword, Punctuation), nil},
|
{`(global)(::)`, ByGroups(Keyword, Punctuation), nil},
|
||||||
{`(bool|byte|char|decimal|double|dynamic|float|int|long|object|sbyte|short|string|uint|ulong|ushort|var)\b\??`, KeywordType, nil},
|
{`(bool|byte|char|decimal|double|dynamic|float|int|long|object|sbyte|short|string|uint|ulong|ushort|var)\b\??`, KeywordType, nil},
|
||||||
{`(class|struct)(\s+)`, ByGroups(Keyword, Text), Push("class")},
|
{`(class|struct)(\s+)`, ByGroups(Keyword, Text), Push("class")},
|
||||||
{`\b([_a-zA-Z]\w*)(\.)`, ByGroups(NameClass, Punctuation), nil},
|
|
||||||
{`(namespace|using)(\s+)`, ByGroups(Keyword, Text), Push("namespace")},
|
{`(namespace|using)(\s+)`, ByGroups(Keyword, Text), Push("namespace")},
|
||||||
{`@?[_a-zA-Z]\w*`, Name, nil},
|
{`@?[_a-zA-Z]\w*`, Name, nil},
|
||||||
},
|
},
|
||||||
|
2
vendor/github.com/alecthomas/chroma/lexers/circular/doc.go
generated
vendored
Normal file
2
vendor/github.com/alecthomas/chroma/lexers/circular/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Package circular exists to break circular dependencies between lexers.
|
||||||
|
package circular
|
@ -1,12 +1,15 @@
|
|||||||
package p
|
package circular
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
. "github.com/alecthomas/chroma" // nolint
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/h"
|
||||||
"github.com/alecthomas/chroma/lexers/internal"
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PHP lexer.
|
// PHP lexer.
|
||||||
var PHP = internal.Register(MustNewLexer(
|
var PHP = internal.Register(DelegatingLexer(h.HTML, MustNewLexer(
|
||||||
&Config{
|
&Config{
|
||||||
Name: "PHP",
|
Name: "PHP",
|
||||||
Aliases: []string{"php", "php3", "php4", "php5"},
|
Aliases: []string{"php", "php3", "php4", "php5"},
|
||||||
@ -80,4 +83,9 @@ var PHP = internal.Register(MustNewLexer(
|
|||||||
{`[${\\]`, LiteralStringDouble, nil},
|
{`[${\\]`, LiteralStringDouble, nil},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
))
|
).SetAnalyser(func(text string) float32 {
|
||||||
|
if strings.Contains(text, "<?php") {
|
||||||
|
return 0.5
|
||||||
|
}
|
||||||
|
return 0.0
|
||||||
|
})))
|
6
vendor/github.com/alecthomas/chroma/lexers/e/elixir.go
generated
vendored
6
vendor/github.com/alecthomas/chroma/lexers/e/elixir.go
generated
vendored
@ -36,9 +36,9 @@ var Elixir = internal.Register(MustNewLexer(
|
|||||||
{`\\\\|\<\<|\>\>|\=\>|\(|\)|\:|\;|\,|\[|\]`, Punctuation, nil},
|
{`\\\\|\<\<|\>\>|\=\>|\(|\)|\:|\;|\,|\[|\]`, Punctuation, nil},
|
||||||
{`&\d`, NameEntity, nil},
|
{`&\d`, NameEntity, nil},
|
||||||
{`\<|\>|\+|\-|\*|\/|\!|\^|\&`, Operator, nil},
|
{`\<|\>|\+|\-|\*|\/|\!|\^|\&`, Operator, nil},
|
||||||
{`0b[01]+`, LiteralNumberBin, nil},
|
{`0b[01](_?[01])*`, LiteralNumberBin, nil},
|
||||||
{`0o[0-7]+`, LiteralNumberOct, nil},
|
{`0o[0-7](_?[0-7])*`, LiteralNumberOct, nil},
|
||||||
{`0x[\da-fA-F]+`, LiteralNumberHex, nil},
|
{`0x[\da-fA-F](_?[\dA-Fa-f])*`, LiteralNumberHex, nil},
|
||||||
{`\d(_?\d)*\.\d(_?\d)*([eE][-+]?\d(_?\d)*)?`, LiteralNumberFloat, nil},
|
{`\d(_?\d)*\.\d(_?\d)*([eE][-+]?\d(_?\d)*)?`, LiteralNumberFloat, nil},
|
||||||
{`\d(_?\d)*`, LiteralNumberInteger, nil},
|
{`\d(_?\d)*`, LiteralNumberInteger, nil},
|
||||||
{`"""\s*`, LiteralStringHeredoc, Push("heredoc_double")},
|
{`"""\s*`, LiteralStringHeredoc, Push("heredoc_double")},
|
||||||
|
60
vendor/github.com/alecthomas/chroma/lexers/g/go.go
generated
vendored
60
vendor/github.com/alecthomas/chroma/lexers/g/go.go
generated
vendored
@ -4,6 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
. "github.com/alecthomas/chroma" // nolint
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/h"
|
||||||
"github.com/alecthomas/chroma/lexers/internal"
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,9 +39,10 @@ var Go = internal.Register(MustNewLexer(
|
|||||||
{`0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil},
|
{`0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil},
|
||||||
{`(0|[1-9][0-9]*)`, LiteralNumberInteger, nil},
|
{`(0|[1-9][0-9]*)`, LiteralNumberInteger, nil},
|
||||||
{`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil},
|
{`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil},
|
||||||
{"`[^`]*`", LiteralString, nil},
|
{"(`)([^`]*)(`)", ByGroups(LiteralString, Using(TypeRemappingLexer(GoTextTemplate, TypeMapping{{Other, LiteralString, nil}})), LiteralString), nil},
|
||||||
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
|
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
|
||||||
{`(<<=|>>=|<<|>>|<=|>=|&\^=|&\^|\+=|-=|\*=|/=|%=|&=|\|=|&&|\|\||<-|\+\+|--|==|!=|:=|\.\.\.|[+\-*/%&])`, Operator, nil},
|
{`(<<=|>>=|<<|>>|<=|>=|&\^=|&\^|\+=|-=|\*=|/=|%=|&=|\|=|&&|\|\||<-|\+\+|--|==|!=|:=|\.\.\.|[+\-*/%&])`, Operator, nil},
|
||||||
|
{`([a-zA-Z_]\w*)(\s*)(\()`, ByGroups(NameFunction, UsingSelf("root"), Punctuation), nil},
|
||||||
{`[|^<>=!()\[\]{}.,;:]`, Punctuation, nil},
|
{`[|^<>=!()\[\]{}.,;:]`, Punctuation, nil},
|
||||||
{`[^\W\d]\w*`, NameOther, nil},
|
{`[^\W\d]\w*`, NameOther, nil},
|
||||||
},
|
},
|
||||||
@ -54,3 +56,59 @@ var Go = internal.Register(MustNewLexer(
|
|||||||
}
|
}
|
||||||
return 0.0
|
return 0.0
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
var goTemplateRules = Rules{
|
||||||
|
"root": {
|
||||||
|
{`{{[-]?`, CommentPreproc, Push("template")},
|
||||||
|
{`[^{]+`, Other, nil},
|
||||||
|
{`{`, Other, nil},
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
{`[-]?}}`, CommentPreproc, Pop(1)},
|
||||||
|
{`/\*.*?\*/`, Comment, nil},
|
||||||
|
{`(?=}})`, CommentPreproc, Pop(1)}, // Terminate the pipeline
|
||||||
|
{`\(`, Operator, Push("subexpression")},
|
||||||
|
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
|
||||||
|
Include("expression"),
|
||||||
|
},
|
||||||
|
"subexpression": {
|
||||||
|
{`\)`, Operator, Pop(1)},
|
||||||
|
Include("expression"),
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
{`\s+`, Whitespace, nil},
|
||||||
|
{`\(`, Operator, Push("subexpression")},
|
||||||
|
{`(range|if|else|while|with|template|end|true|false|nil|and|call|html|index|js|len|not|or|print|printf|println|urlquery|eq|ne|lt|le|gt|ge)\b`, Keyword, nil},
|
||||||
|
{`\||:=`, Operator, nil},
|
||||||
|
{`[$]?[^\W\d]\w*`, NameOther, nil},
|
||||||
|
{`[$]?\.(?:[^\W\d]\w*)?`, NameAttribute, nil},
|
||||||
|
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
|
||||||
|
{`\d+i`, LiteralNumber, nil},
|
||||||
|
{`\d+\.\d*([Ee][-+]\d+)?i`, LiteralNumber, nil},
|
||||||
|
{`\.\d+([Ee][-+]\d+)?i`, LiteralNumber, nil},
|
||||||
|
{`\d+[Ee][-+]\d+i`, LiteralNumber, nil},
|
||||||
|
{`\d+(\.\d+[eE][+\-]?\d+|\.\d*|[eE][+\-]?\d+)`, LiteralNumberFloat, nil},
|
||||||
|
{`\.\d+([eE][+\-]?\d+)?`, LiteralNumberFloat, nil},
|
||||||
|
{`0[0-7]+`, LiteralNumberOct, nil},
|
||||||
|
{`0[xX][0-9a-fA-F]+`, LiteralNumberHex, nil},
|
||||||
|
{`(0|[1-9][0-9]*)`, LiteralNumberInteger, nil},
|
||||||
|
{`'(\\['"\\abfnrtv]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|[^\\])'`, LiteralStringChar, nil},
|
||||||
|
{"`[^`]*`", LiteralString, nil},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var GoHTMLTemplate = internal.Register(DelegatingLexer(h.HTML, MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "Go HTML Template",
|
||||||
|
Aliases: []string{"go-html-template"},
|
||||||
|
},
|
||||||
|
goTemplateRules,
|
||||||
|
)))
|
||||||
|
|
||||||
|
var GoTextTemplate = internal.Register(MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "Go Text Template",
|
||||||
|
Aliases: []string{"go-text-template"},
|
||||||
|
},
|
||||||
|
goTemplateRules,
|
||||||
|
))
|
||||||
|
17
vendor/github.com/alecthomas/chroma/lexers/h/http.go
generated
vendored
17
vendor/github.com/alecthomas/chroma/lexers/h/http.go
generated
vendored
@ -34,7 +34,7 @@ var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer(
|
|||||||
)))
|
)))
|
||||||
|
|
||||||
func httpContentBlock(groups []string, lexer Lexer) Iterator {
|
func httpContentBlock(groups []string, lexer Lexer) Iterator {
|
||||||
tokens := []*Token{
|
tokens := []Token{
|
||||||
{Generic, groups[0]},
|
{Generic, groups[0]},
|
||||||
}
|
}
|
||||||
return Literator(tokens...)
|
return Literator(tokens...)
|
||||||
@ -42,7 +42,7 @@ func httpContentBlock(groups []string, lexer Lexer) Iterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func httpHeaderBlock(groups []string, lexer Lexer) Iterator {
|
func httpHeaderBlock(groups []string, lexer Lexer) Iterator {
|
||||||
tokens := []*Token{
|
tokens := []Token{
|
||||||
{Name, groups[1]},
|
{Name, groups[1]},
|
||||||
{Text, groups[2]},
|
{Text, groups[2]},
|
||||||
{Operator, groups[3]},
|
{Operator, groups[3]},
|
||||||
@ -54,7 +54,7 @@ func httpHeaderBlock(groups []string, lexer Lexer) Iterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func httpContinuousHeaderBlock(groups []string, lexer Lexer) Iterator {
|
func httpContinuousHeaderBlock(groups []string, lexer Lexer) Iterator {
|
||||||
tokens := []*Token{
|
tokens := []Token{
|
||||||
{Text, groups[1]},
|
{Text, groups[1]},
|
||||||
{Literal, groups[2]},
|
{Literal, groups[2]},
|
||||||
{Text, groups[3]},
|
{Text, groups[3]},
|
||||||
@ -76,8 +76,8 @@ func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return func() *Token {
|
return func() Token {
|
||||||
for token := it(); token != nil; token = it() {
|
for token := it(); token != EOF; token = it() {
|
||||||
switch {
|
switch {
|
||||||
case token.Type == Name && strings.ToLower(token.Value) == "content-type":
|
case token.Type == Name && strings.ToLower(token.Value) == "content-type":
|
||||||
{
|
{
|
||||||
@ -85,6 +85,7 @@ func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) (
|
|||||||
}
|
}
|
||||||
case token.Type == Literal && isContentType:
|
case token.Type == Literal && isContentType:
|
||||||
{
|
{
|
||||||
|
isContentType = false
|
||||||
contentType = strings.TrimSpace(token.Value)
|
contentType = strings.TrimSpace(token.Value)
|
||||||
pos := strings.Index(contentType, ";")
|
pos := strings.Index(contentType, ";")
|
||||||
if pos > 0 {
|
if pos > 0 {
|
||||||
@ -111,7 +112,7 @@ func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return nil
|
return EOF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,11 +122,11 @@ func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if subIterator != nil {
|
if subIterator != nil {
|
||||||
for token := subIterator(); token != nil; token = subIterator() {
|
for token := subIterator(); token != EOF; token = subIterator() {
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return EOF
|
||||||
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
2
vendor/github.com/alecthomas/chroma/lexers/i/ini.go
generated
vendored
2
vendor/github.com/alecthomas/chroma/lexers/i/ini.go
generated
vendored
@ -10,7 +10,7 @@ var Ini = internal.Register(MustNewLexer(
|
|||||||
&Config{
|
&Config{
|
||||||
Name: "INI",
|
Name: "INI",
|
||||||
Aliases: []string{"ini", "cfg", "dosini"},
|
Aliases: []string{"ini", "cfg", "dosini"},
|
||||||
Filenames: []string{"*.ini", "*.cfg", "*.inf"},
|
Filenames: []string{"*.ini", "*.cfg", "*.inf", ".gitconfig"},
|
||||||
MimeTypes: []string{"text/x-ini", "text/inf"},
|
MimeTypes: []string{"text/x-ini", "text/inf"},
|
||||||
},
|
},
|
||||||
Rules{
|
Rules{
|
||||||
|
15
vendor/github.com/alecthomas/chroma/lexers/internal/api.go
generated
vendored
15
vendor/github.com/alecthomas/chroma/lexers/internal/api.go
generated
vendored
@ -144,13 +144,16 @@ func Register(lexer chroma.Lexer) chroma.Lexer {
|
|||||||
return lexer
|
return lexer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback lexer if no other is found.
|
// Used for the fallback lexer as well as the explicit plaintext lexer
|
||||||
var Fallback chroma.Lexer = chroma.MustNewLexer(&chroma.Config{
|
var PlaintextRules = chroma.Rules{
|
||||||
Name: "fallback",
|
|
||||||
Filenames: []string{"*"},
|
|
||||||
}, chroma.Rules{
|
|
||||||
"root": []chroma.Rule{
|
"root": []chroma.Rule{
|
||||||
{`.+`, chroma.Text, nil},
|
{`.+`, chroma.Text, nil},
|
||||||
{`\n`, chroma.Text, nil},
|
{`\n`, chroma.Text, nil},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Fallback lexer if no other is found.
|
||||||
|
var Fallback chroma.Lexer = chroma.MustNewLexer(&chroma.Config{
|
||||||
|
Name: "fallback",
|
||||||
|
Filenames: []string{"*"},
|
||||||
|
}, PlaintextRules)
|
||||||
|
4
vendor/github.com/alecthomas/chroma/lexers/j/json.go
generated
vendored
4
vendor/github.com/alecthomas/chroma/lexers/j/json.go
generated
vendored
@ -5,8 +5,8 @@ import (
|
|||||||
"github.com/alecthomas/chroma/lexers/internal"
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Json lexer.
|
// JSON lexer.
|
||||||
var Json = internal.Register(MustNewLexer(
|
var JSON = internal.Register(MustNewLexer(
|
||||||
&Config{
|
&Config{
|
||||||
Name: "JSON",
|
Name: "JSON",
|
||||||
Aliases: []string{"json"},
|
Aliases: []string{"json"},
|
||||||
|
50
vendor/github.com/alecthomas/chroma/lexers/j/jungle.go
generated
vendored
Normal file
50
vendor/github.com/alecthomas/chroma/lexers/j/jungle.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package j
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Jungle = internal.Register(MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "Jungle",
|
||||||
|
Aliases: []string{"jungle"},
|
||||||
|
Filenames: []string{"*.jungle"},
|
||||||
|
MimeTypes: []string{"text/x-jungle"},
|
||||||
|
},
|
||||||
|
Rules{
|
||||||
|
"root": {
|
||||||
|
{`[^\S\n]+`, Text, nil},
|
||||||
|
{`\n`, Text, nil},
|
||||||
|
{`#(\n|[\w\W]*?[^#]\n)`, CommentSingle, nil},
|
||||||
|
{`^(?=\S)`, None, Push("instruction")},
|
||||||
|
{`[\.;\[\]\(\)\$]`, Punctuation, nil},
|
||||||
|
{`[a-zA-Z_]\w*`, Name, nil},
|
||||||
|
},
|
||||||
|
"instruction": {
|
||||||
|
{`[^\S\n]+`, Text, nil},
|
||||||
|
{`=`, Operator, Push("value")},
|
||||||
|
{`(?=\S)`, None, Push("var")},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
{`[^\S\n]+`, Text, nil},
|
||||||
|
{`\$\(`, Punctuation, Push("var")},
|
||||||
|
{`[;\[\]\(\)\$]`, Punctuation, nil},
|
||||||
|
{`#(\n|[\w\W]*?[^#]\n)`, CommentSingle, nil},
|
||||||
|
{`[\w_\-\.\/\\]+`, Text, nil},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
"var": {
|
||||||
|
{`[^\S\n]+`, Text, nil},
|
||||||
|
{`\b(((re)?source|barrel)Path|excludeAnnotations|annotations|lang)\b`, NameBuiltin, nil},
|
||||||
|
{`\bbase\b`, NameConstant, nil},
|
||||||
|
{`\b(ind|zsm|hrv|ces|dan|dut|eng|fin|fre|deu|gre|hun|ita|nob|po[lr]|rus|sl[ov]|spa|swe|ara|heb|zh[st]|jpn|kor|tha|vie|bul|tur)`, NameConstant, nil},
|
||||||
|
{`\b((semi)?round|rectangle)(-\d+x\d+)?\b`, NameConstant, nil},
|
||||||
|
{`[\.;\[\]\(\$]`, Punctuation, nil},
|
||||||
|
{`\)`, Punctuation, Pop(1)},
|
||||||
|
{`[a-zA-Z_]\w*`, Name, nil},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
1
vendor/github.com/alecthomas/chroma/lexers/lexers.go
generated
vendored
1
vendor/github.com/alecthomas/chroma/lexers/lexers.go
generated
vendored
@ -9,6 +9,7 @@ import (
|
|||||||
_ "github.com/alecthomas/chroma/lexers/a"
|
_ "github.com/alecthomas/chroma/lexers/a"
|
||||||
_ "github.com/alecthomas/chroma/lexers/b"
|
_ "github.com/alecthomas/chroma/lexers/b"
|
||||||
_ "github.com/alecthomas/chroma/lexers/c"
|
_ "github.com/alecthomas/chroma/lexers/c"
|
||||||
|
_ "github.com/alecthomas/chroma/lexers/circular"
|
||||||
_ "github.com/alecthomas/chroma/lexers/d"
|
_ "github.com/alecthomas/chroma/lexers/d"
|
||||||
_ "github.com/alecthomas/chroma/lexers/e"
|
_ "github.com/alecthomas/chroma/lexers/e"
|
||||||
_ "github.com/alecthomas/chroma/lexers/f"
|
_ "github.com/alecthomas/chroma/lexers/f"
|
||||||
|
34
vendor/github.com/alecthomas/chroma/lexers/m/markdown.go
generated
vendored
34
vendor/github.com/alecthomas/chroma/lexers/m/markdown.go
generated
vendored
@ -21,8 +21,15 @@ var Markdown = internal.Register(MustNewLexer(
|
|||||||
{`^(\s*)([*-])(\s)(.+\n)`, ByGroups(Text, Keyword, Text, UsingSelf("inline")), nil},
|
{`^(\s*)([*-])(\s)(.+\n)`, ByGroups(Text, Keyword, Text, UsingSelf("inline")), nil},
|
||||||
{`^(\s*)([0-9]+\.)( .+\n)`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
|
{`^(\s*)([0-9]+\.)( .+\n)`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
|
||||||
{`^(\s*>\s)(.+\n)`, ByGroups(Keyword, GenericEmph), nil},
|
{`^(\s*>\s)(.+\n)`, ByGroups(Keyword, GenericEmph), nil},
|
||||||
{"^(```\\n)([\\w\\W]*?)(^```$)", ByGroups(LiteralString, Text, LiteralString), nil},
|
{"^(```\\n)([\\w\\W]*?)(^```$)", ByGroups(String, Text, String), nil},
|
||||||
{"^(```)(\\w+)(\\n)([\\w\\W]*?)(^```$)", EmitterFunc(markdownCodeBlock), nil},
|
{"^(```)(\\w+)(\\n)([\\w\\W]*?)(^```$)",
|
||||||
|
UsingByGroup(
|
||||||
|
internal.Get,
|
||||||
|
2, 4,
|
||||||
|
String, String, String, Text, String,
|
||||||
|
),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
Include("inline"),
|
Include("inline"),
|
||||||
},
|
},
|
||||||
"inline": {
|
"inline": {
|
||||||
@ -38,26 +45,3 @@ var Markdown = internal.Register(MustNewLexer(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|
||||||
func markdownCodeBlock(groups []string, lexer Lexer) Iterator {
|
|
||||||
iterators := []Iterator{}
|
|
||||||
tokens := []*Token{
|
|
||||||
{String, groups[1]},
|
|
||||||
{String, groups[2]},
|
|
||||||
{Text, groups[3]},
|
|
||||||
}
|
|
||||||
code := groups[4]
|
|
||||||
lexer = internal.Get(groups[2])
|
|
||||||
if lexer == nil {
|
|
||||||
tokens = append(tokens, &Token{String, code})
|
|
||||||
iterators = append(iterators, Literator(tokens...))
|
|
||||||
} else {
|
|
||||||
sub, err := lexer.Tokenise(nil, code)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
iterators = append(iterators, Literator(tokens...), sub)
|
|
||||||
}
|
|
||||||
iterators = append(iterators, Literator(&Token{String, groups[5]}))
|
|
||||||
return Concaterator(iterators...)
|
|
||||||
}
|
|
||||||
|
62
vendor/github.com/alecthomas/chroma/lexers/m/monkeyc.go
generated
vendored
Normal file
62
vendor/github.com/alecthomas/chroma/lexers/m/monkeyc.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package m
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var MonkeyC = internal.Register(MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "MonkeyC",
|
||||||
|
Aliases: []string{"monkeyc"},
|
||||||
|
Filenames: []string{"*.mc"},
|
||||||
|
MimeTypes: []string{"text/x-monkeyc"},
|
||||||
|
},
|
||||||
|
Rules{
|
||||||
|
"root": {
|
||||||
|
{`[^\S\n]+`, Text, nil},
|
||||||
|
{`\n`, Text, nil},
|
||||||
|
{`//(\n|[\w\W]*?[^\\]\n)`, CommentSingle, nil},
|
||||||
|
{`/(\\\n)?[*][\w\W]*?[*](\\\n)?/`, CommentMultiline, nil},
|
||||||
|
{`/(\\\n)?[*][\w\W]*`, CommentMultiline, nil},
|
||||||
|
{`:[a-zA-Z_][\w_\.]*`, StringSymbol, nil},
|
||||||
|
{`[{}\[\]\(\),;:\.]`, Punctuation, nil},
|
||||||
|
{`[&~\|\^!+\-*\/%=?]`, Operator, nil},
|
||||||
|
{`=>|[+-]=|&&|\|\||>>|<<|[<>]=?|[!=]=`, Operator, nil},
|
||||||
|
{`\b(and|or|instanceof|has|extends|new)`, OperatorWord, nil},
|
||||||
|
{Words(``, `\b`, `NaN`, `null`, `true`, `false`), KeywordConstant, nil},
|
||||||
|
{`(using)((?:\s|\\\\s)+)`, ByGroups(KeywordNamespace, Text), Push("import")},
|
||||||
|
{`(class)((?:\s|\\\\s)+)`, ByGroups(KeywordDeclaration, Text), Push("class")},
|
||||||
|
{`(function)((?:\s|\\\\s)+)`, ByGroups(KeywordDeclaration, Text), Push("function")},
|
||||||
|
{`(module)((?:\s|\\\\s)+)`, ByGroups(KeywordDeclaration, Text), Push("module")},
|
||||||
|
{`\b(if|else|for|switch|case|while|break|continue|default|do|try|catch|finally|return|throw|extends|function)\b`, Keyword, nil},
|
||||||
|
{`\b(const|enum|hidden|public|protected|private|static)\b`, KeywordType, nil},
|
||||||
|
{`\bvar\b`, KeywordDeclaration, nil},
|
||||||
|
{`\b(Activity(Monitor|Recording)?|Ant(Plus)?|Application|Attention|Background|Communications|Cryptography|FitContributor|Graphics|Gregorian|Lang|Math|Media|Persisted(Content|Locations)|Position|Properties|Sensor(History|Logging)?|Storage|StringUtil|System|Test|Time(r)?|Toybox|UserProfile|WatchUi|Rez|Drawables|Strings|Fonts|method)\b`, NameBuiltin, nil},
|
||||||
|
{`\b(me|self|\$)\b`, NameBuiltinPseudo, nil},
|
||||||
|
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
|
||||||
|
{`'(\\\\|\\'|[^''])*'`, LiteralStringSingle, nil},
|
||||||
|
{`-?(0x[0-9a-fA-F]+l?)`, NumberHex, nil},
|
||||||
|
{`-?([0-9]+(\.[0-9]+[df]?|[df]))\b`, NumberFloat, nil},
|
||||||
|
{`-?([0-9]+l?)`, NumberInteger, nil},
|
||||||
|
{`[a-zA-Z_]\w*`, Name, nil},
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
{`([a-zA-Z_][\w_\.]*)(?:(\s+)(as)(\s+)([a-zA-Z_][\w_]*))?`, ByGroups(NameNamespace, Text, KeywordNamespace, Text, NameNamespace), nil},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
"class": {
|
||||||
|
{`([a-zA-Z_][\w_\.]*)(?:(\s+)(extends)(\s+)([a-zA-Z_][\w_\.]*))?`, ByGroups(NameClass, Text, KeywordDeclaration, Text, NameClass), nil},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
"function": {
|
||||||
|
{`initialize`, NameFunctionMagic, nil},
|
||||||
|
{`[a-zA-Z_][\w_\.]*`, NameFunction, nil},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
"module": {
|
||||||
|
{`[a-zA-Z_][\w_\.]*`, NameNamespace, nil},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
1
vendor/github.com/alecthomas/chroma/lexers/n/nim.go
generated
vendored
1
vendor/github.com/alecthomas/chroma/lexers/n/nim.go
generated
vendored
@ -31,6 +31,7 @@ var Nim = internal.Register(MustNewLexer(
|
|||||||
{`(v_?a_?r)\b`, KeywordDeclaration, nil},
|
{`(v_?a_?r)\b`, KeywordDeclaration, nil},
|
||||||
{`(i_?n_?t_?|i_?n_?t_?8_?|i_?n_?t_?1_?6_?|i_?n_?t_?3_?2_?|i_?n_?t_?6_?4_?|f_?l_?o_?a_?t_?|f_?l_?o_?a_?t_?3_?2_?|f_?l_?o_?a_?t_?6_?4_?|b_?o_?o_?l_?|c_?h_?a_?r_?|r_?a_?n_?g_?e_?|a_?r_?r_?a_?y_?|s_?e_?q_?|s_?e_?t_?|s_?t_?r_?i_?n_?g_?)\b`, KeywordType, nil},
|
{`(i_?n_?t_?|i_?n_?t_?8_?|i_?n_?t_?1_?6_?|i_?n_?t_?3_?2_?|i_?n_?t_?6_?4_?|f_?l_?o_?a_?t_?|f_?l_?o_?a_?t_?3_?2_?|f_?l_?o_?a_?t_?6_?4_?|b_?o_?o_?l_?|c_?h_?a_?r_?|r_?a_?n_?g_?e_?|a_?r_?r_?a_?y_?|s_?e_?q_?|s_?e_?t_?|s_?t_?r_?i_?n_?g_?)\b`, KeywordType, nil},
|
||||||
{`(n_?i_?l_?|t_?r_?u_?e_?|f_?a_?l_?s_?e_?)\b`, KeywordPseudo, nil},
|
{`(n_?i_?l_?|t_?r_?u_?e_?|f_?a_?l_?s_?e_?)\b`, KeywordPseudo, nil},
|
||||||
|
{`\b_\b`, Name, nil}, // Standalone _ used as discardable variable identifier
|
||||||
{`\b((?![_\d])\w)(((?!_)\w)|(_(?!_)\w))*`, Name, nil},
|
{`\b((?![_\d])\w)(((?!_)\w)|(_(?!_)\w))*`, Name, nil},
|
||||||
{`[0-9][0-9_]*(?=([e.]|\'f(32|64)))`, LiteralNumberFloat, Push("float-suffix", "float-number")},
|
{`[0-9][0-9_]*(?=([e.]|\'f(32|64)))`, LiteralNumberFloat, Push("float-suffix", "float-number")},
|
||||||
{`0x[a-f0-9][a-f0-9_]*`, LiteralNumberHex, Push("int-suffix")},
|
{`0x[a-f0-9][a-f0-9_]*`, LiteralNumberHex, Push("int-suffix")},
|
||||||
|
43
vendor/github.com/alecthomas/chroma/lexers/o/openscad.go
generated
vendored
Normal file
43
vendor/github.com/alecthomas/chroma/lexers/o/openscad.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package o
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var OpenSCAD = internal.Register(MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "OpenSCAD",
|
||||||
|
Aliases: []string{"openscad"},
|
||||||
|
Filenames: []string{"*.scad"},
|
||||||
|
MimeTypes: []string{"text/x-scad"},
|
||||||
|
},
|
||||||
|
Rules{
|
||||||
|
"root": {
|
||||||
|
{`[^\S\n]+`, Text, nil},
|
||||||
|
{`\n`, Text, nil},
|
||||||
|
{`//(\n|[\w\W]*?[^\\]\n)`, CommentSingle, nil},
|
||||||
|
{`/(\\\n)?[*][\w\W]*?[*](\\\n)?/`, CommentMultiline, nil},
|
||||||
|
{`/(\\\n)?[*][\w\W]*`, CommentMultiline, nil},
|
||||||
|
{`[{}\[\]\(\),;:]`, Punctuation, nil},
|
||||||
|
{`[*!#%\-+=?/]`, Operator, nil},
|
||||||
|
{`<|<=|==|!=|>=|>|&&|\|\|`, Operator, nil},
|
||||||
|
{`\$(f[asn]|t|vp[rtd]|children)`, NameVariableMagic, nil},
|
||||||
|
{Words(``, `\b`, `PI`, `undef`), KeywordConstant, nil},
|
||||||
|
{`(use|include)((?:\s|\\\\s)+)`, ByGroups(KeywordNamespace, Text), Push("includes")},
|
||||||
|
{`(module)(\s*)([^\s\(]+)`, ByGroups(KeywordNamespace, Text, NameNamespace), nil},
|
||||||
|
{`(function)(\s*)([^\s\(]+)`, ByGroups(KeywordDeclaration, Text, NameFunction), nil},
|
||||||
|
{`\b(true|false)\b`, Literal, nil},
|
||||||
|
{`\b(function|module|include|use|for|intersection_for|if|else|return)\b`, Keyword, nil},
|
||||||
|
{`\b(circle|square|polygon|text|sphere|cube|cylinder|polyhedron|translate|rotate|scale|resize|mirror|multmatrix|color|offset|hull|minkowski|union|difference|intersection|abs|sign|sin|cos|tan|acos|asin|atan|atan2|floor|round|ceil|ln|log|pow|sqrt|exp|rands|min|max|concat|lookup|str|chr|search|version|version_num|norm|cross|parent_module|echo|import|import_dxf|dxf_linear_extrude|linear_extrude|rotate_extrude|surface|projection|render|dxf_cross|dxf_dim|let|assign|len)\b`, NameBuiltin, nil},
|
||||||
|
{`\bchildren\b`, NameBuiltinPseudo, nil},
|
||||||
|
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
|
||||||
|
{`-?\d+(\.\d+)?(e[+-]?\d+)?`, Number, nil},
|
||||||
|
{`[a-zA-Z_]\w*`, Name, nil},
|
||||||
|
},
|
||||||
|
"includes": {
|
||||||
|
{"(<)([^>]*)(>)", ByGroups(Punctuation, CommentPreprocFile, Punctuation), nil},
|
||||||
|
Default(Pop(1)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
102
vendor/github.com/alecthomas/chroma/lexers/o/org.go
generated
vendored
Normal file
102
vendor/github.com/alecthomas/chroma/lexers/o/org.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package o
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/alecthomas/chroma" // nolint
|
||||||
|
"github.com/alecthomas/chroma/lexers/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Org mode lexer.
|
||||||
|
var Org = internal.Register(MustNewLexer(
|
||||||
|
&Config{
|
||||||
|
Name: "Org Mode",
|
||||||
|
Aliases: []string{"org", "orgmode"},
|
||||||
|
Filenames: []string{"*.org"},
|
||||||
|
MimeTypes: []string{"text/org"}, // https://lists.gnu.org/r/emacs-orgmode/2017-09/msg00087.html
|
||||||
|
},
|
||||||
|
Rules{
|
||||||
|
"root": {
|
||||||
|
{`^# .*$`, Comment, nil},
|
||||||
|
// Headings
|
||||||
|
{`^(\*)( COMMENT)( .*)$`, ByGroups(GenericHeading, NameEntity, GenericStrong), nil},
|
||||||
|
{`^(\*\*+)( COMMENT)( .*)$`, ByGroups(GenericSubheading, NameEntity, Text), nil},
|
||||||
|
{`^(\*)( DONE)( .*)$`, ByGroups(GenericHeading, LiteralStringRegex, GenericStrong), nil},
|
||||||
|
{`^(\*\*+)( DONE)( .*)$`, ByGroups(GenericSubheading, LiteralStringRegex, Text), nil},
|
||||||
|
{`^(\*)( TODO)( .*)$`, ByGroups(GenericHeading, Error, GenericStrong), nil},
|
||||||
|
{`^(\*\*+)( TODO)( .*)$`, ByGroups(GenericSubheading, Error, Text), nil},
|
||||||
|
{`^(\*)( .+?)( :[a-zA-Z0-9_@:]+:)$`, ByGroups(GenericHeading, GenericStrong, GenericEmph), nil}, // Level 1 heading with tags
|
||||||
|
{`^(\*)( .+)$`, ByGroups(GenericHeading, GenericStrong), nil}, // // Level 1 heading with NO tags
|
||||||
|
{`^(\*\*+)( .+?)( :[a-zA-Z0-9_@:]+:)$`, ByGroups(GenericSubheading, Text, GenericEmph), nil}, // Level 2+ heading with tags
|
||||||
|
{`^(\*\*+)( .+)$`, ByGroups(GenericSubheading, Text), nil}, // Level 2+ heading with NO tags
|
||||||
|
// Checkbox lists
|
||||||
|
{`^( *)([+-] )(\[[ X]\])( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
|
||||||
|
{`^( +)(\* )(\[[ X]\])( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
|
||||||
|
// Definition lists
|
||||||
|
{`^( *)([+-] )([^ \n]+ ::)( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
|
||||||
|
{`^( +)(\* )([^ \n]+ ::)( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
|
||||||
|
// Unordered lists
|
||||||
|
{`^( *)([+-] )(.+)$`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
|
||||||
|
{`^( +)(\* )(.+)$`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
|
||||||
|
// Ordered lists
|
||||||
|
{`^( *)([0-9]+[.)])( \[@[0-9]+\])( .+)$`, ByGroups(Text, Keyword, GenericEmph, UsingSelf("inline")), nil},
|
||||||
|
{`^( *)([0-9]+[.)])( .+)$`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
|
||||||
|
// Dynamic Blocks
|
||||||
|
{`(?i)^( *#\+begin: )([^ ]+)([\w\W]*?\n)([\w\W]*?)(^ *#\+end: *$)`, ByGroups(Comment, CommentSpecial, Comment, UsingSelf("inline"), Comment), nil},
|
||||||
|
// Blocks
|
||||||
|
// - Comment Blocks
|
||||||
|
{`(?i)^( *#\+begin_comment *\n)([\w\W]*?)(^ *#\+end_comment *$)`, ByGroups(Comment, Comment, Comment), nil},
|
||||||
|
// - Src Blocks
|
||||||
|
{`(?i)^( *#\+begin_src )([^ \n]+)(.*?\n)([\w\W]*?)(^ *#\+end_src *$)`,
|
||||||
|
UsingByGroup(
|
||||||
|
internal.Get,
|
||||||
|
2, 4,
|
||||||
|
Comment, CommentSpecial, Comment, Text, Comment,
|
||||||
|
),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
// - Export Blocks
|
||||||
|
{`(?i)^( *#\+begin_export )(\w+)( *\n)([\w\W]*?)(^ *#\+end_export *$)`,
|
||||||
|
UsingByGroup(
|
||||||
|
internal.Get,
|
||||||
|
2, 4,
|
||||||
|
Comment, CommentSpecial, Text, Text, Comment,
|
||||||
|
),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
// - Org Special, Example, Verse, etc. Blocks
|
||||||
|
{`(?i)^( *#\+begin_)(\w+)( *\n)([\w\W]*?)(^ *#\+end_\2)( *$)`, ByGroups(Comment, Comment, Text, Text, Comment, Text), nil},
|
||||||
|
// Keywords
|
||||||
|
{`^(#\+\w+)(:.*)$`, ByGroups(CommentSpecial, Comment), nil}, // Other Org keywords like #+title
|
||||||
|
// Properties and Drawers
|
||||||
|
{`(?i)^( *:\w+: *\n)([\w\W]*?)(^ *:end: *$)`, ByGroups(Comment, CommentSpecial, Comment), nil},
|
||||||
|
// Line break operator
|
||||||
|
{`^(.*)(\\\\)$`, ByGroups(UsingSelf("inline"), Operator), nil},
|
||||||
|
// Deadline/Scheduled
|
||||||
|
{`(?i)^( *(?:DEADLINE|SCHEDULED): )(<[^<>]+?> *)$`, ByGroups(Comment, CommentSpecial), nil}, // DEADLINE/SCHEDULED: <datestamp>
|
||||||
|
// DONE state CLOSED
|
||||||
|
{`(?i)^( *CLOSED: )(\[[^][]+?\] *)$`, ByGroups(Comment, CommentSpecial), nil}, // CLOSED: [datestamp]
|
||||||
|
// All other lines
|
||||||
|
Include("inline"),
|
||||||
|
},
|
||||||
|
"inline": {
|
||||||
|
{`(\s)*(\*[^ \n*][^*]+?[^ \n*]\*)((?=\W|\n|$))`, ByGroups(Text, GenericStrong, Text), nil}, // Bold
|
||||||
|
{`(\s)*(/[^/]+?/)((?=\W|\n|$))`, ByGroups(Text, GenericEmph, Text), nil}, // Italic
|
||||||
|
{`(\s)*(=[^\n=]+?=)((?=\W|\n|$))`, ByGroups(Text, NameClass, Text), nil}, // Verbatim
|
||||||
|
{`(\s)*(~[^\n~]+?~)((?=\W|\n|$))`, ByGroups(Text, NameClass, Text), nil}, // Code
|
||||||
|
{`(\s)*(\+[^+]+?\+)((?=\W|\n|$))`, ByGroups(Text, GenericDeleted, Text), nil}, // Strikethrough
|
||||||
|
{`(\s)*(_[^_]+?_)((?=\W|\n|$))`, ByGroups(Text, GenericUnderline, Text), nil}, // Underline
|
||||||
|
{`(<)([^<>]+?)(>)`, ByGroups(Text, String, Text), nil}, // <datestamp>
|
||||||
|
{`[{]{3}[^}]+[}]{3}`, NameBuiltin, nil}, // {{{macro(foo,1)}}}
|
||||||
|
{`([^[])(\[fn:)([^]]+?)(\])([^]])`, ByGroups(Text, NameBuiltinPseudo, LiteralString, NameBuiltinPseudo, Text), nil}, // [fn:1]
|
||||||
|
// Links
|
||||||
|
{`(\[\[)([^][]+?)(\]\[)([^][]+)(\]\])`, ByGroups(Text, NameAttribute, Text, NameTag, Text), nil}, // [[link][descr]]
|
||||||
|
{`(\[\[)([^][]+?)(\]\])`, ByGroups(Text, NameAttribute, Text), nil}, // [[link]]
|
||||||
|
{`(<<)([^<>]+?)(>>)`, ByGroups(Text, NameAttribute, Text), nil}, // <<targetlink>>
|
||||||
|
// Tables
|
||||||
|
{`^( *)(\|[ -].*?[ -]\|)$`, ByGroups(Text, String), nil},
|
||||||
|
// Blank lines, newlines
|
||||||
|
{`\n`, Text, nil},
|
||||||
|
// Any other text
|
||||||
|
{`.`, Text, nil},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
))
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user