Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
034882c0e5 | |||
cd843ccc11 | |||
e7dac3d397 | |||
99ff8180f8 | |||
e0c8c59b1b | |||
4bba0d2600 | |||
456dc6856d | |||
6dceedf978 | |||
4523393c2a | |||
6c301cff0c | |||
b73c06f1ab | |||
0668e42ea8 | |||
f2e38fda23 | |||
ae81ead712 | |||
9262f436b6 | |||
343af57742 | |||
e216e2a1b5 |
8
.github/workflows/docker-tag.yaml
vendored
8
.github/workflows/docker-tag.yaml
vendored
@ -17,12 +17,12 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to Docker registry
|
||||
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.xyz -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.com -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
|
||||
- name: Build and push Docker image
|
||||
run: |
|
||||
docker build -t git.jabuxas.xyz/jabuxas/abyss:${{ github.ref_name }} .
|
||||
docker push git.jabuxas.xyz/jabuxas/abyss:${{ github.ref_name }}
|
||||
docker build -t git.jabuxas.com/jabuxas/abyss:${{ github.ref_name }} .
|
||||
docker push git.jabuxas.com/jabuxas/abyss:${{ github.ref_name }}
|
||||
|
||||
- name: Log out of Docker registry
|
||||
run: docker logout git.jabuxas.xyz
|
||||
run: docker logout git.jabuxas.com
|
||||
|
8
.github/workflows/docker.yaml
vendored
8
.github/workflows/docker.yaml
vendored
@ -20,12 +20,12 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to Docker registry
|
||||
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.xyz -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.com -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
|
||||
- name: Build and push Docker image
|
||||
run: |
|
||||
docker build -t git.jabuxas.xyz/jabuxas/abyss:latest .
|
||||
docker push git.jabuxas.xyz/jabuxas/abyss:latest
|
||||
docker build -t git.jabuxas.com/jabuxas/abyss:latest .
|
||||
docker push git.jabuxas.com/jabuxas/abyss:latest
|
||||
|
||||
- name: Log out of Docker registry
|
||||
run: docker logout git.jabuxas.xyz
|
||||
run: docker logout git.jabuxas.com
|
||||
|
@ -1,6 +1,6 @@
|
||||
# abyss
|
||||
|
||||
abyss is a basic and mostly single user http server written in go made for uploading files (logs, images) and then sharing them to the internet
|
||||
abyss is a basic and _mostly_ single user pastebin server written in go made for uploading files (logs, images) and then sharing them to the internet
|
||||
|
||||
<figure>
|
||||
<img src="https://github.com/user-attachments/assets/eae42368-d8b5-4c42-ac8a-0e1486fcd0d4" alt="homepage"/>
|
||||
@ -49,7 +49,7 @@ abyss is a basic and mostly single user http server written in go made for uploa
|
||||
docker compose up -d # might be docker-compose depending on distro
|
||||
```
|
||||
|
||||
- you can optionally use the [docker image](https://git.jabuxas.xyz/jabuxas/-/packages/container/abyss/latest) directly and set it up how you want
|
||||
- you can optionally use the [docker image](https://git.jabuxas.com/jabuxas/-/packages/container/abyss/latest) directly and set it up how you want
|
||||
|
||||
### directly
|
||||
|
||||
@ -78,6 +78,8 @@ abyss is a basic and mostly single user http server written in go made for uploa
|
||||
curl -F "file=@/path/to/file" -H "X-Auth: "$(cat /path/to/.key) http://localhost:3235/
|
||||
```
|
||||
|
||||
- it is also possible to add a `-Fsecret=` to your POST to make filenames bigger and harder to guess.
|
||||
|
||||
- you should probably create an `alias` or a `function` to do this automatically for you.
|
||||
<details>
|
||||
<summary>click for an example for bash/zsh:</summary>
|
||||
@ -166,7 +168,7 @@ abyss is a basic and mostly single user http server written in go made for uploa
|
||||
- `UPLOAD_KEY`: this is key checked when uploading files. if the key doesn't match with server's one, then it refuses uploading.
|
||||
- `ABYSS_FILEDIR`: this points to the directory where abyss will save the uploads to. defaults to `./files`
|
||||
- `ABYSS_PORT`: this is the port the server will run on. safe to leave empty. defaults to 3235
|
||||
- `SHOULD_AUTH`: if it is `yes`, then to upload text through the browser you will need authentication (same auth as `/tree`), any value other than that and upload is auth-less
|
||||
- `SHOULD_AUTH`: if it is `yes`, then to upload text you will need authentication (same auth as `/tree`), any value other than that and upload is authless
|
||||
|
||||
## todo:
|
||||
|
||||
|
12
abyss.go
12
abyss.go
@ -95,17 +95,9 @@ func setupHandlers(mux *http.ServeMux, app *Application) {
|
||||
),
|
||||
)
|
||||
|
||||
mux.HandleFunc("/last", app.lastUploadedHandler)
|
||||
mux.HandleFunc("/last", BasicAuth(app.lastUploadedHandler, app))
|
||||
|
||||
mux.HandleFunc("/token", BasicAuth(app.createTokenHandler, app))
|
||||
|
||||
mux.HandleFunc("/files/", app.fileHandler)
|
||||
|
||||
if app.authUpload == "yes" {
|
||||
mux.HandleFunc("/upload", BasicAuth(app.uploadHandler, app))
|
||||
slog.Warn("text uploading through the browser will be restricted")
|
||||
} else {
|
||||
mux.HandleFunc("/upload", app.uploadHandler)
|
||||
slog.Warn("text uploading through the browser will NOT be restricted")
|
||||
}
|
||||
mux.HandleFunc("/raw/", app.fileHandler)
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ var extensions = map[string]string{
|
||||
|
||||
".png": "image", ".jpg": "image", ".jpeg": "image", ".webp": "image",
|
||||
|
||||
".mp3": "audio", ".aac": "audio", ".wav": "audio", ".flac": "audio", ".ogg": "audio",
|
||||
|
||||
".sh": "text", ".bash": "text", ".zsh": "text",
|
||||
".bat": "text", ".cmd": "text", ".ps1": "text",
|
||||
".ini": "text", ".cfg": "text", ".conf": "text",
|
||||
@ -45,8 +47,10 @@ func DisplayFile(app *Application, file string, w http.ResponseWriter) {
|
||||
tmpl = template.Must(template.ParseFS(filesTemplate, "templates/files.html"))
|
||||
}
|
||||
|
||||
fileStat, _ := os.Stat("." + file)
|
||||
fileContent, _ := os.ReadFile("." + file)
|
||||
realPath := filepath.Join(app.filesDir, filepath.Base(file))
|
||||
|
||||
fileStat, _ := os.Stat("./" + realPath)
|
||||
fileContent, _ := os.ReadFile("./" + realPath)
|
||||
|
||||
fileInfo := FileInfo{
|
||||
Name: file,
|
||||
|
@ -43,7 +43,7 @@ AUTH_USERNAME=$AUTH_USERNAME
|
||||
# This is the password of the user for accessing /tree
|
||||
AUTH_PASSWORD=$AUTH_PASSWORD
|
||||
|
||||
# This is whether you need a password to upload text through the browser
|
||||
# This is whether you need a password to upload text (through browser or curl)
|
||||
SHOULD_AUTH=$SHOULD_AUTH
|
||||
|
||||
# This is the key needed to make uploads. Include it as X-Auth in curl.
|
||||
|
50
handlers.go
50
handlers.go
@ -81,15 +81,16 @@ func (app *Application) fileListingHandler(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
func (app *Application) fileHandler(w http.ResponseWriter, r *http.Request) {
|
||||
path := fmt.Sprintf(".%s", filepath.Clean(r.URL.Path))
|
||||
path := fmt.Sprintf("%s", filepath.Base(r.URL.Path))
|
||||
realPath := filepath.Join(app.filesDir, path)
|
||||
|
||||
if !filepath.IsLocal(path) {
|
||||
if !filepath.IsLocal(realPath) {
|
||||
http.Error(w, "Wrong url", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if fileInfo, err := os.Stat(path); err == nil && !fileInfo.IsDir() {
|
||||
http.ServeFile(w, r, path)
|
||||
if fileInfo, err := os.Stat(realPath); err == nil && !fileInfo.IsDir() {
|
||||
http.ServeFile(w, r, realPath)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -106,16 +107,16 @@ func (app *Application) indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
name := filepath.Clean(r.URL.Path)
|
||||
path := filepath.Join(app.filesDir, name)
|
||||
name := filepath.Base(r.URL.Path)
|
||||
realPath := filepath.Join(app.filesDir, name)
|
||||
|
||||
if !filepath.IsLocal(path) {
|
||||
if !filepath.IsLocal(realPath) || strings.Contains(r.URL.Path, filepath.Clean(app.filesDir)) {
|
||||
http.Error(w, "Wrong url", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if fileInfo, err := os.Stat(path); err == nil && !fileInfo.IsDir() {
|
||||
DisplayFile(app, "/"+path, w)
|
||||
if fileInfo, err := os.Stat(realPath); err == nil && !fileInfo.IsDir() {
|
||||
DisplayFile(app, filepath.Join("/raw", name), w)
|
||||
return
|
||||
}
|
||||
|
||||
@ -132,12 +133,16 @@ func (app *Application) lastUploadedHandler(w http.ResponseWriter, r *http.Reque
|
||||
http.Error(w, "No new files uploaded yet", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
DisplayFile(app, "/"+app.lastUploadedFile, w)
|
||||
DisplayFile(app, filepath.Join("/raw", filepath.Base(app.lastUploadedFile)), w)
|
||||
}
|
||||
|
||||
func (app *Application) uploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if contentType := r.Header.Get("Content-Type"); contentType == "application/x-www-form-urlencoded" {
|
||||
app.formHandler(w, r)
|
||||
if app.authUpload == "yes" {
|
||||
BasicAuth(app.formHandler, app)(w, r)
|
||||
} else {
|
||||
app.formHandler(w, r)
|
||||
}
|
||||
} else if strings.Split(contentType, ";")[0] == "multipart/form-data" {
|
||||
app.curlHandler(w, r)
|
||||
} else {
|
||||
@ -158,7 +163,11 @@ func (app *Application) formHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filename := app.publicURL(file, ".txt")
|
||||
full := true
|
||||
if len(r.Form["secret"]) == 0 {
|
||||
full = false
|
||||
}
|
||||
filename := app.publicURL(file, ".txt", full)
|
||||
|
||||
// reopening file because hash consumes it
|
||||
file, err = os.Open("/tmp/file.txt")
|
||||
@ -172,7 +181,7 @@ func (app *Application) formHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Error parsing file: %s", err.Error())
|
||||
}
|
||||
|
||||
ResponseURLHandler(w, app.url, filename)
|
||||
ResponseURLHandler(r, w, app.url, filename)
|
||||
}
|
||||
|
||||
func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@ -194,7 +203,11 @@ func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filename := app.publicURL(file, filepath.Ext(handler.Filename))
|
||||
full := true
|
||||
if len(r.Form["secret"]) == 0 {
|
||||
full = false
|
||||
}
|
||||
filename := app.publicURL(file, filepath.Ext(handler.Filename), full)
|
||||
|
||||
// reopen the file for copying, as the hash process consumed the file reader
|
||||
file, _, err = r.FormFile("file")
|
||||
@ -204,16 +217,15 @@ func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = SaveFile(app.lastUploadedFile, file)
|
||||
if err != nil {
|
||||
if err = SaveFile(app.lastUploadedFile, file); err != nil {
|
||||
fmt.Fprintf(w, "Error parsing file: %s", err.Error())
|
||||
}
|
||||
|
||||
ResponseURLHandler(w, app.url, filename)
|
||||
ResponseURLHandler(r, w, app.url, filename)
|
||||
}
|
||||
|
||||
func (app *Application) publicURL(file io.Reader, extension string) string {
|
||||
filename, _ := HashFile(file, extension)
|
||||
func (app *Application) publicURL(file io.Reader, extension string, full bool) string {
|
||||
filename, _ := HashFile(file, extension, full)
|
||||
|
||||
filepath := filepath.Join(app.filesDir, filename)
|
||||
|
||||
|
23
helpers.go
23
helpers.go
@ -9,6 +9,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
@ -67,17 +68,19 @@ func FormatFileSize(size int64) string {
|
||||
return fmt.Sprintf("%.2f GB", float64(size)/(1024*1024*1024))
|
||||
}
|
||||
|
||||
func HashFile(file io.Reader, extension string) (string, error) {
|
||||
func HashFile(file io.Reader, extension string, full bool) (string, error) {
|
||||
hasher := md5.New()
|
||||
if _, err := io.Copy(hasher, file); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sha1Hash := hex.EncodeToString(hasher.Sum(nil))[:8]
|
||||
|
||||
sha1Hash := strings.ToUpper(hex.EncodeToString(hasher.Sum(nil)))
|
||||
filename := fmt.Sprintf("%s%s", sha1Hash, extension)
|
||||
|
||||
return filename, nil
|
||||
if full {
|
||||
return filename, nil
|
||||
} else {
|
||||
return fmt.Sprintf("%s%s", sha1Hash[:5], extension), nil
|
||||
}
|
||||
}
|
||||
|
||||
func SaveFile(name string, file io.Reader) error {
|
||||
@ -120,12 +123,14 @@ func BasicAuth(next http.HandlerFunc, app *Application) http.HandlerFunc {
|
||||
})
|
||||
}
|
||||
|
||||
func ResponseURLHandler(w http.ResponseWriter, url, filename string) {
|
||||
pasteURL := fmt.Sprintf("http://%s/%s\n", url, filename)
|
||||
func ResponseURLHandler(r *http.Request, w http.ResponseWriter, url, filename string) {
|
||||
protocol := "http"
|
||||
if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" {
|
||||
protocol = "https"
|
||||
}
|
||||
pasteURL := fmt.Sprintf("%s://%s/%s\n", protocol, url, filename)
|
||||
|
||||
w.Header().Set("Location", pasteURL)
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprintf(w, "%s", pasteURL)
|
||||
}
|
||||
|
@ -20,8 +20,9 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form action="/upload" method="POST">
|
||||
<textarea name="content" placeholder="Enter your content here..."></textarea><br />
|
||||
<form action="/" method="POST">
|
||||
<textarea name="content" placeholder="Enter your content here..."></textarea>
|
||||
<br />
|
||||
<button type="submit">upload</button>
|
||||
</form>
|
||||
|
||||
|
@ -133,6 +133,10 @@
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
@ -193,14 +197,31 @@
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0288d1;
|
||||
color: #d4d4d4;
|
||||
text-decoration: none;
|
||||
filter: brightness(0.7);
|
||||
transition: filter 0.2s;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
filter: brightness(1)
|
||||
}
|
||||
|
||||
.path {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
left: -1.85%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>{{.Path}}</header>
|
||||
<header>
|
||||
<nav>
|
||||
<a href="/">Home</a>
|
||||
</nav>
|
||||
<a href="{{.Name}}" class="path">{{.Path}}</a>
|
||||
</header>
|
||||
<div class="content">
|
||||
{{if eq .Type "text"}}
|
||||
<pre>{{.Content}}</pre>
|
||||
@ -213,12 +234,14 @@
|
||||
<source src="{{.Name}}" type="video/mp4" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
{{else}}
|
||||
<p>
|
||||
Couldn't detect file from extension, visit
|
||||
<a href="http://{{.Path}}">this link</a> to see/download your file.
|
||||
</p>
|
||||
{{end}}
|
||||
{{else if eq .Type "audio"}}
|
||||
<audio controls src="{{.Name}}"><audio />
|
||||
{{else}}
|
||||
<p>
|
||||
Couldn't detect file from extension, visit
|
||||
<a href="http://{{.Path}}">this link</a> to see/download your file.
|
||||
</p>
|
||||
{{end}}
|
||||
</div>
|
||||
<footer>file uploaded in {{.TimeUploaded}}</footer>
|
||||
</body>
|
||||
|
Loading…
Reference in New Issue
Block a user