diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 8bf4d45..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index b9c05c5..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/plakken.iml b/.idea/plakken.iml deleted file mode 100644 index 7f4273e..0000000 --- a/.idea/plakken.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/config.go b/config.go index d9c0ab4..8630a3a 100644 --- a/config.go +++ b/config.go @@ -7,7 +7,7 @@ import ( "strconv" ) -type config struct { +type Config struct { host string port string redisAddr string @@ -17,7 +17,7 @@ type config struct { urlLength int } -func getConfig() config { +func GetConfig() Config { err := godotenv.Load() if err != nil { log.Fatalf("Error loading .env file: %v", err) @@ -42,7 +42,7 @@ func getConfig() config { log.Fatal("Invalid PLAKKEN_REDIS_URL_LEN") } - conf := config{ + return Config{ host: os.Getenv("PLAKKEN_INTERFACE"), port: port, redisAddr: redisAddr, @@ -51,6 +51,4 @@ func getConfig() config { redisDB: redisDB, urlLength: urlLen, } - - return conf } diff --git a/db.go b/db.go index 5dcdd03..d6b60b7 100644 --- a/db.go +++ b/db.go @@ -9,7 +9,7 @@ import ( var ctx = context.Background() -func connectDB() *redis.Client { +func ConnectDB() *redis.Client { localDb := redis.NewClient(&redis.Options{ Addr: currentConfig.redisAddr, Username: currentConfig.redisUser, @@ -40,8 +40,7 @@ func insertPaste(key string, content string, secret string, ttl time.Duration) { } func getContent(key string) string { - content := db.HGet(ctx, key, "content").Val() - return content + return db.HGet(ctx, key, "content").Val() } func deleteContent(key string) { diff --git a/main.go b/main.go index aa7c81c..c4cd240 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,7 @@ import ( "strings" ) -var currentConfig config +var currentConfig Config var db *redis.Client type pasteView struct { @@ -21,16 +21,17 @@ type pasteView struct { func handleRequest(w http.ResponseWriter, r *http.Request) { path := r.URL.Path clearPath := strings.ReplaceAll(r.URL.Path, "/raw", "") + staticResource := "/static/" switch r.Method { case "GET": if path == "/" { http.ServeFile(w, r, "./static/index.html") - } else if strings.HasPrefix(path, "/static/") { + } else if strings.HasPrefix(path, staticResource) { fs := http.FileServer(http.Dir("./static")) - http.Handle("/static/", http.StripPrefix("/static/", fs)) + http.Handle(staticResource, http.StripPrefix(staticResource, fs)) } else { - if urlExist(clearPath) { + if UrlExist(clearPath) { if strings.HasSuffix(path, "/raw") { pasteContent := getContent(clearPath) w.Header().Set("Content-Type", "text/plain") @@ -56,8 +57,8 @@ func handleRequest(w http.ResponseWriter, r *http.Request) { } case "POST": if path == "/" { - secret := generateSecret() - url := "/" + generateUrl() + secret := GenerateSecret() + url := "/" + GenerateUrl() content := r.FormValue("content") insertPaste(url, content, secret, -1) http.Redirect(w, r, url, http.StatusSeeOther) @@ -65,28 +66,26 @@ func handleRequest(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusMethodNotAllowed) } case "DELETE": - if strings.HasPrefix(path, "/delete") { - urlItem := strings.Split(path, "/") - if urlExist("/" + urlItem[2]) { - secret := r.URL.Query().Get("secret") - if verifySecret("/"+urlItem[2], secret) { - deleteContent("/" + urlItem[2]) - w.WriteHeader(http.StatusNoContent) - } else { - w.WriteHeader(http.StatusForbidden) + if UrlExist(path) { + secret := r.URL.Query().Get("secret") + if secret == db.HGet(ctx, path, "secret").Val() { + err := db.Del(ctx, path) + if err != nil { + log.Println(err) } + w.WriteHeader(http.StatusNoContent) } else { - w.WriteHeader(http.StatusNotFound) + w.WriteHeader(http.StatusForbidden) } } else { - w.WriteHeader(http.StatusMethodNotAllowed) + w.WriteHeader(http.StatusNotFound) } } } func main() { - db = connectDB() - currentConfig = getConfig() + db = ConnectDB() + currentConfig = GetConfig() listen := currentConfig.host + ":" + currentConfig.port http.HandleFunc("/", handleRequest) diff --git a/static/app.js b/static/app.js index e69de29..bef2c56 100644 --- a/static/app.js +++ b/static/app.js @@ -0,0 +1,31 @@ +const codeEditor = document.getElementById('content'); +const lineCounter = document.getElementById('lines'); + +let lineCountCache = 0; + +// Update line counter +function updateLineCounter() { + const lineCount = codeEditor.value.split('\n').length; + + if (lineCountCache !== lineCount) { + const outarr = Array.from({length: lineCount}, (_, index) => index + 1); + lineCounter.value = outarr.join('\n'); + } + + lineCountCache = lineCount; +} + +codeEditor.addEventListener('input', updateLineCounter); + +codeEditor.addEventListener('keydown', (e) => { + if (e.key === 'Tab') { + e.preventDefault(); + + const {value, selectionStart, selectionEnd} = codeEditor; + codeEditor.value = `${value.slice(0, selectionStart)}\t${value.slice(selectionEnd)}`; + codeEditor.setSelectionRange(selectionStart + 1, selectionStart + 1); + updateLineCounter(); + } +}); + +updateLineCounter(); diff --git a/static/index.html b/static/index.html index 10504d4..40eb943 100644 --- a/static/index.html +++ b/static/index.html @@ -9,50 +9,57 @@ New plak • Plakken +
- - - +
+ + +
+
+ + + +
\ No newline at end of file diff --git a/static/style.css b/static/style.css index d605146..4005e5a 100644 --- a/static/style.css +++ b/static/style.css @@ -2,7 +2,7 @@ --accent: #be0560; --background: #141414; --border: #333; - --text: #e2e2e2; + --text: #e9e9e9; } body { @@ -12,19 +12,35 @@ body { margin: 0; } +form { + display: flex; + flex-flow: row wrap; +} + button, textarea { background-color: inherit; border: none; + outline: none; + resize: none; } -textarea { - color: var(--text); - font: 14px/1.6 "JetBrains Mono", monospace; +#lines { + color: #999; + font: 400 14px/1.6 "JetBrains Mono", monospace; height: calc(100vh - 3rem); - outline: none; - padding: 1rem; - resize: none; - width: calc(100vw - 2rem); + overflow-y: hidden; + padding: 10px 0; + text-align: center; + user-select: none; + width: 26px; +} + +#content { + color: var(--text); + font: 400 14px/1.6 "JetBrains Mono", monospace; + height: calc(100vh - 3rem); + padding: 10px 10px 10px 6px; + width: calc(100vw - 42px); } pre { @@ -43,7 +59,7 @@ nav { ul { display: flex; flex-flow: row wrap; - gap: 2.6rem; + gap: 36px; list-style: none; margin: 0; padding: 0 1.9rem; @@ -60,7 +76,7 @@ input, select { color: var(--text); font-size: 15px; outline: none; - padding: 5px 6px; + padding: 6px 8px; transition: border .15s ease; } @@ -86,7 +102,7 @@ input:focus-visible, select:focus-visible { border: 2px solid var(--text); } -button:focus-visible{ +button:focus-visible { outline: none; } diff --git a/utils.go b/utils.go index 36305da..2b2753e 100644 --- a/utils.go +++ b/utils.go @@ -7,7 +7,7 @@ import ( mathrand "math/rand" ) -func generateUrl() string { +func GenerateUrl() string { listChars := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") b := make([]rune, currentConfig.urlLength) for i := range b { @@ -17,7 +17,7 @@ func generateUrl() string { return string(b) } -func generateSecret() string { +func GenerateSecret() string { key := make([]byte, 32) _, err := rand.Read(key) if err != nil { @@ -27,14 +27,10 @@ func generateSecret() string { return hex.EncodeToString(key) } -func urlExist(url string) bool { - exist := db.Exists(ctx, url).Val() - return exist == 1 +func UrlExist(url string) bool { + return db.Exists(ctx, url).Val() == 1 } -func verifySecret(url string, secret string) bool { - if secret == db.HGet(ctx, url, "secret").Val() { - return true - } - return false +func VerifySecret(url string, secret string) bool { + return secret == db.HGet(ctx, url, "secret").Val() }