Compare commits

..

No commits in common. "main" and "v1.3.0" have entirely different histories.
main ... v1.3.0

10 changed files with 62 additions and 101 deletions

View File

@ -17,12 +17,12 @@ jobs:
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Log in to Docker registry - name: Log in to Docker registry
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.com -u ${{ secrets.DOCKER_USERNAME }} --password-stdin run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.xyz -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
- name: Build and push Docker image - name: Build and push Docker image
run: | run: |
docker build -t git.jabuxas.com/jabuxas/abyss:${{ github.ref_name }} . docker build -t git.jabuxas.xyz/jabuxas/abyss:${{ github.ref_name }} .
docker push git.jabuxas.com/jabuxas/abyss:${{ github.ref_name }} docker push git.jabuxas.xyz/jabuxas/abyss:${{ github.ref_name }}
- name: Log out of Docker registry - name: Log out of Docker registry
run: docker logout git.jabuxas.com run: docker logout git.jabuxas.xyz

View File

@ -20,12 +20,12 @@ jobs:
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Log in to Docker registry - name: Log in to Docker registry
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.com -u ${{ secrets.DOCKER_USERNAME }} --password-stdin run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login git.jabuxas.xyz -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
- name: Build and push Docker image - name: Build and push Docker image
run: | run: |
docker build -t git.jabuxas.com/jabuxas/abyss:latest . docker build -t git.jabuxas.xyz/jabuxas/abyss:latest .
docker push git.jabuxas.com/jabuxas/abyss:latest docker push git.jabuxas.xyz/jabuxas/abyss:latest
- name: Log out of Docker registry - name: Log out of Docker registry
run: docker logout git.jabuxas.com run: docker logout git.jabuxas.xyz

View File

@ -1,6 +1,6 @@
# abyss # abyss
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 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
<figure> <figure>
<img src="https://github.com/user-attachments/assets/eae42368-d8b5-4c42-ac8a-0e1486fcd0d4" alt="homepage"/> <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 pastebin server written in go made for
docker compose up -d # might be docker-compose depending on distro docker compose up -d # might be docker-compose depending on distro
``` ```
- you can optionally use the [docker image](https://git.jabuxas.com/jabuxas/-/packages/container/abyss/latest) directly and set it up how you want - you can optionally use the [docker image](https://git.jabuxas.xyz/jabuxas/-/packages/container/abyss/latest) directly and set it up how you want
### directly ### directly
@ -78,8 +78,6 @@ abyss is a basic and _mostly_ single user pastebin server written in go made for
curl -F "file=@/path/to/file" -H "X-Auth: "$(cat /path/to/.key) http://localhost:3235/ 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. - you should probably create an `alias` or a `function` to do this automatically for you.
<details> <details>
<summary>click for an example for bash/zsh:</summary> <summary>click for an example for bash/zsh:</summary>
@ -168,7 +166,7 @@ abyss is a basic and _mostly_ single user pastebin server written in go made for
- `UPLOAD_KEY`: this is key checked when uploading files. if the key doesn't match with server's one, then it refuses uploading. - `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_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 - `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 you will need authentication (same auth as `/tree`), any value other than that and upload is authless - `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
## todo: ## todo:

View File

@ -95,9 +95,17 @@ func setupHandlers(mux *http.ServeMux, app *Application) {
), ),
) )
mux.HandleFunc("/last", BasicAuth(app.lastUploadedHandler, app)) mux.HandleFunc("/last", app.lastUploadedHandler)
mux.HandleFunc("/token", BasicAuth(app.createTokenHandler, app)) mux.HandleFunc("/token", BasicAuth(app.createTokenHandler, app))
mux.HandleFunc("/raw/", app.fileHandler) 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")
}
} }

View File

@ -17,8 +17,6 @@ var extensions = map[string]string{
".png": "image", ".jpg": "image", ".jpeg": "image", ".webp": "image", ".png": "image", ".jpg": "image", ".jpeg": "image", ".webp": "image",
".mp3": "audio", ".aac": "audio", ".wav": "audio", ".flac": "audio", ".ogg": "audio",
".sh": "text", ".bash": "text", ".zsh": "text", ".sh": "text", ".bash": "text", ".zsh": "text",
".bat": "text", ".cmd": "text", ".ps1": "text", ".bat": "text", ".cmd": "text", ".ps1": "text",
".ini": "text", ".cfg": "text", ".conf": "text", ".ini": "text", ".cfg": "text", ".conf": "text",
@ -47,10 +45,8 @@ func DisplayFile(app *Application, file string, w http.ResponseWriter) {
tmpl = template.Must(template.ParseFS(filesTemplate, "templates/files.html")) tmpl = template.Must(template.ParseFS(filesTemplate, "templates/files.html"))
} }
realPath := filepath.Join(app.filesDir, filepath.Base(file)) fileStat, _ := os.Stat("." + file)
fileContent, _ := os.ReadFile("." + file)
fileStat, _ := os.Stat("./" + realPath)
fileContent, _ := os.ReadFile("./" + realPath)
fileInfo := FileInfo{ fileInfo := FileInfo{
Name: file, Name: file,

View File

@ -43,7 +43,7 @@ AUTH_USERNAME=$AUTH_USERNAME
# This is the password of the user for accessing /tree # This is the password of the user for accessing /tree
AUTH_PASSWORD=$AUTH_PASSWORD AUTH_PASSWORD=$AUTH_PASSWORD
# This is whether you need a password to upload text (through browser or curl) # This is whether you need a password to upload text through the browser
SHOULD_AUTH=$SHOULD_AUTH SHOULD_AUTH=$SHOULD_AUTH
# This is the key needed to make uploads. Include it as X-Auth in curl. # This is the key needed to make uploads. Include it as X-Auth in curl.

View File

@ -81,16 +81,15 @@ func (app *Application) fileListingHandler(w http.ResponseWriter, r *http.Reques
} }
func (app *Application) fileHandler(w http.ResponseWriter, r *http.Request) { func (app *Application) fileHandler(w http.ResponseWriter, r *http.Request) {
path := fmt.Sprintf("%s", filepath.Base(r.URL.Path)) path := fmt.Sprintf(".%s", filepath.Clean(r.URL.Path))
realPath := filepath.Join(app.filesDir, path)
if !filepath.IsLocal(realPath) { if !filepath.IsLocal(path) {
http.Error(w, "Wrong url", http.StatusBadRequest) http.Error(w, "Wrong url", http.StatusBadRequest)
return return
} }
if fileInfo, err := os.Stat(realPath); err == nil && !fileInfo.IsDir() { if fileInfo, err := os.Stat(path); err == nil && !fileInfo.IsDir() {
http.ServeFile(w, r, realPath) http.ServeFile(w, r, path)
return return
} }
} }
@ -107,16 +106,16 @@ func (app *Application) indexHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
name := filepath.Base(r.URL.Path) name := filepath.Clean(r.URL.Path)
realPath := filepath.Join(app.filesDir, name) path := filepath.Join(app.filesDir, name)
if !filepath.IsLocal(realPath) || strings.Contains(r.URL.Path, filepath.Clean(app.filesDir)) { if !filepath.IsLocal(path) {
http.Error(w, "Wrong url", http.StatusBadRequest) http.Error(w, "Wrong url", http.StatusBadRequest)
return return
} }
if fileInfo, err := os.Stat(realPath); err == nil && !fileInfo.IsDir() { if fileInfo, err := os.Stat(path); err == nil && !fileInfo.IsDir() {
DisplayFile(app, filepath.Join("/raw", name), w) DisplayFile(app, "/"+path, w)
return return
} }
@ -133,16 +132,12 @@ func (app *Application) lastUploadedHandler(w http.ResponseWriter, r *http.Reque
http.Error(w, "No new files uploaded yet", http.StatusNotFound) http.Error(w, "No new files uploaded yet", http.StatusNotFound)
return return
} }
DisplayFile(app, filepath.Join("/raw", filepath.Base(app.lastUploadedFile)), w) DisplayFile(app, "/"+app.lastUploadedFile, w)
} }
func (app *Application) uploadHandler(w http.ResponseWriter, r *http.Request) { func (app *Application) uploadHandler(w http.ResponseWriter, r *http.Request) {
if contentType := r.Header.Get("Content-Type"); contentType == "application/x-www-form-urlencoded" { if contentType := r.Header.Get("Content-Type"); contentType == "application/x-www-form-urlencoded" {
if app.authUpload == "yes" {
BasicAuth(app.formHandler, app)(w, r)
} else {
app.formHandler(w, r) app.formHandler(w, r)
}
} else if strings.Split(contentType, ";")[0] == "multipart/form-data" { } else if strings.Split(contentType, ";")[0] == "multipart/form-data" {
app.curlHandler(w, r) app.curlHandler(w, r)
} else { } else {
@ -163,11 +158,7 @@ func (app *Application) formHandler(w http.ResponseWriter, r *http.Request) {
} }
defer file.Close() defer file.Close()
full := true filename := app.publicURL(file, ".txt")
if len(r.Form["secret"]) == 0 {
full = false
}
filename := app.publicURL(file, ".txt", full)
// reopening file because hash consumes it // reopening file because hash consumes it
file, err = os.Open("/tmp/file.txt") file, err = os.Open("/tmp/file.txt")
@ -181,7 +172,7 @@ func (app *Application) formHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Error parsing file: %s", err.Error()) fmt.Fprintf(w, "Error parsing file: %s", err.Error())
} }
ResponseURLHandler(r, w, app.url, filename) ResponseURLHandler(w, app.url, filename)
} }
func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) { func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) {
@ -203,11 +194,7 @@ func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) {
} }
defer file.Close() defer file.Close()
full := true filename := app.publicURL(file, filepath.Ext(handler.Filename))
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 // reopen the file for copying, as the hash process consumed the file reader
file, _, err = r.FormFile("file") file, _, err = r.FormFile("file")
@ -217,15 +204,16 @@ func (app *Application) curlHandler(w http.ResponseWriter, r *http.Request) {
} }
defer file.Close() defer file.Close()
if err = SaveFile(app.lastUploadedFile, file); err != nil { err = SaveFile(app.lastUploadedFile, file)
if err != nil {
fmt.Fprintf(w, "Error parsing file: %s", err.Error()) fmt.Fprintf(w, "Error parsing file: %s", err.Error())
} }
ResponseURLHandler(r, w, app.url, filename) ResponseURLHandler(w, app.url, filename)
} }
func (app *Application) publicURL(file io.Reader, extension string, full bool) string { func (app *Application) publicURL(file io.Reader, extension string) string {
filename, _ := HashFile(file, extension, full) filename, _ := HashFile(file, extension)
filepath := filepath.Join(app.filesDir, filename) filepath := filepath.Join(app.filesDir, filename)

View File

@ -9,7 +9,6 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"strings"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
) )
@ -68,19 +67,17 @@ func FormatFileSize(size int64) string {
return fmt.Sprintf("%.2f GB", float64(size)/(1024*1024*1024)) return fmt.Sprintf("%.2f GB", float64(size)/(1024*1024*1024))
} }
func HashFile(file io.Reader, extension string, full bool) (string, error) { func HashFile(file io.Reader, extension string) (string, error) {
hasher := md5.New() hasher := md5.New()
if _, err := io.Copy(hasher, file); err != nil { if _, err := io.Copy(hasher, file); err != nil {
return "", err return "", err
} }
sha1Hash := strings.ToUpper(hex.EncodeToString(hasher.Sum(nil))) sha1Hash := hex.EncodeToString(hasher.Sum(nil))[:8]
filename := fmt.Sprintf("%s%s", sha1Hash, extension) filename := fmt.Sprintf("%s%s", sha1Hash, extension)
if full {
return filename, nil return filename, nil
} else {
return fmt.Sprintf("%s%s", sha1Hash[:5], extension), nil
}
} }
func SaveFile(name string, file io.Reader) error { func SaveFile(name string, file io.Reader) error {
@ -123,14 +120,12 @@ func BasicAuth(next http.HandlerFunc, app *Application) http.HandlerFunc {
}) })
} }
func ResponseURLHandler(r *http.Request, w http.ResponseWriter, url, filename string) { func ResponseURLHandler(w http.ResponseWriter, url, filename string) {
protocol := "http" pasteURL := fmt.Sprintf("http://%s/%s\n", url, filename)
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.Header().Set("Location", pasteURL)
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, "%s", pasteURL) fmt.Fprintf(w, "%s", pasteURL)
} }

View File

@ -20,9 +20,8 @@
</a> </a>
</div> </div>
<form action="/" method="POST"> <form action="/upload" method="POST">
<textarea name="content" placeholder="Enter your content here..."></textarea> <textarea name="content" placeholder="Enter your content here..."></textarea><br />
<br />
<button type="submit">upload</button> <button type="submit">upload</button>
</form> </form>

View File

@ -133,10 +133,6 @@
padding: 10px; padding: 10px;
} }
header {
display: flex;
}
.content { .content {
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
@ -197,31 +193,14 @@
} }
a { a {
color: #d4d4d4; color: #0288d1;
text-decoration: none; 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> </style>
</head> </head>
<body> <body>
<header> <header>{{.Path}}</header>
<nav>
<a href="/">Home</a>
</nav>
<a href="{{.Name}}" class="path">{{.Path}}</a>
</header>
<div class="content"> <div class="content">
{{if eq .Type "text"}} {{if eq .Type "text"}}
<pre>{{.Content}}</pre> <pre>{{.Content}}</pre>
@ -234,8 +213,6 @@
<source src="{{.Name}}" type="video/mp4" /> <source src="{{.Name}}" type="video/mp4" />
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video>
{{else if eq .Type "audio"}}
<audio controls src="{{.Name}}"><audio />
{{else}} {{else}}
<p> <p>
Couldn't detect file from extension, visit Couldn't detect file from extension, visit