Add 404 page, add Deleted page, fix errors
This commit is contained in:
@@ -30,6 +30,9 @@ func uploadHandler(c *gin.Context) {
|
|||||||
id := uuid.New().String()
|
id := uuid.New().String()
|
||||||
delID := uuid.New().String()
|
delID := uuid.New().String()
|
||||||
cleanName := filepath.Base(header.Filename)
|
cleanName := filepath.Base(header.Filename)
|
||||||
|
if len(cleanName) > 255 {
|
||||||
|
cleanName = cleanName[:255]
|
||||||
|
}
|
||||||
|
|
||||||
folderPath := filepath.Join("uploads", id)
|
folderPath := filepath.Join("uploads", id)
|
||||||
os.MkdirAll(folderPath, 0755)
|
os.MkdirAll(folderPath, 0755)
|
||||||
@@ -65,6 +68,7 @@ func uploadHandler(c *gin.Context) {
|
|||||||
Filename: cleanName,
|
Filename: cleanName,
|
||||||
Path: storagePath,
|
Path: storagePath,
|
||||||
ExpiresAt: expiry,
|
ExpiresAt: expiry,
|
||||||
|
Size: written,
|
||||||
DeleteAfterDownload: c.PostForm("once") == "true",
|
DeleteAfterDownload: c.PostForm("once") == "true",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,14 +83,21 @@ func uploadHandler(c *gin.Context) {
|
|||||||
|
|
||||||
func downloadHandler(c *gin.Context) {
|
func downloadHandler(c *gin.Context) {
|
||||||
var record FileRecord
|
var record FileRecord
|
||||||
if err := db.First(&record, "id = ? AND deleted = ?", c.Param("id"), false).Error; err != nil {
|
var err = db.First(&record, "id = ? AND deleted = ?", c.Param("id"), false).Error
|
||||||
c.String(404, "File not found or expired")
|
if err != nil {
|
||||||
|
c.HTML(200, "fileNotFound.html", gin.H{
|
||||||
|
"message": "File not found",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(record.ExpiresAt) {
|
if time.Now().After(record.ExpiresAt) {
|
||||||
performDeletion(&record)
|
performDeletion(&record)
|
||||||
c.String(410, "File has expired")
|
|
||||||
|
//c.String(410, "File has expired")
|
||||||
|
c.HTML(404, "fileNotFound.html", gin.H{
|
||||||
|
"message": "File not found",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +118,8 @@ func deleteHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
performDeletion(&record)
|
performDeletion(&record)
|
||||||
c.JSON(200, gin.H{"message": "Deleted successfully"})
|
//c.JSON(200, gin.H{"message": "Deleted successfully"})
|
||||||
|
c.HTML(200, "deleted.html", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginHandler(c *gin.Context) {
|
func loginHandler(c *gin.Context) {
|
||||||
@@ -145,3 +157,30 @@ func loginHandler(c *gin.Context) {
|
|||||||
|
|
||||||
c.Redirect(302, "/admin")
|
c.Redirect(302, "/admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func adminIndexHandler(c *gin.Context) {
|
||||||
|
var files []FileRecord
|
||||||
|
|
||||||
|
// Pagination parameters
|
||||||
|
perPage := 20
|
||||||
|
page := 1
|
||||||
|
if p := c.Query("page"); p != "" {
|
||||||
|
if v, err := strconv.Atoi(p); err == nil && v > 0 {
|
||||||
|
page = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
db.Model(&FileRecord{}).Count(&total)
|
||||||
|
|
||||||
|
totalPages := int((total + int64(perPage) - 1) / int64(perPage)) // ceiling division
|
||||||
|
|
||||||
|
offset := (page - 1) * perPage
|
||||||
|
db.Order("created_at desc").Limit(perPage).Offset(offset).Find(&files)
|
||||||
|
|
||||||
|
c.HTML(200, "admin.html", gin.H{
|
||||||
|
"Files": files,
|
||||||
|
"Page": page,
|
||||||
|
"TotalPages": totalPages,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
41
src/main.go
41
src/main.go
@@ -6,7 +6,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@@ -34,8 +33,9 @@ func main() {
|
|||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
router.MaxMultipartMemory = 10 << 30
|
router.MaxMultipartMemory = 10 << 30
|
||||||
router.SetFuncMap(template.FuncMap{
|
router.SetFuncMap(template.FuncMap{
|
||||||
"add": func(a, b int) int { return a + b },
|
"add": func(a, b int) int { return a + b },
|
||||||
"sub": func(a, b int) int { return a - b },
|
"sub": func(a, b int) int { return a - b },
|
||||||
|
"humanSize": humanSize,
|
||||||
})
|
})
|
||||||
router.LoadHTMLGlob("templates/*")
|
router.LoadHTMLGlob("templates/*")
|
||||||
var staticPath = gin.Dir("./static", false)
|
var staticPath = gin.Dir("./static", false)
|
||||||
@@ -43,6 +43,9 @@ func main() {
|
|||||||
|
|
||||||
router.StaticFS("/static", staticPath)
|
router.StaticFS("/static", staticPath)
|
||||||
|
|
||||||
|
router.NoRoute(func(c *gin.Context) {
|
||||||
|
c.HTML(404, "error.html", nil)
|
||||||
|
})
|
||||||
// Public Routes
|
// Public Routes
|
||||||
router.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", nil) })
|
router.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", nil) })
|
||||||
router.GET("/f/:id", downloadHandler)
|
router.GET("/f/:id", downloadHandler)
|
||||||
@@ -53,34 +56,16 @@ func main() {
|
|||||||
admin := router.Group("/admin")
|
admin := router.Group("/admin")
|
||||||
admin.Use(authMiddleware())
|
admin.Use(authMiddleware())
|
||||||
|
|
||||||
admin.GET("/", func(c *gin.Context) {
|
admin.GET("/", adminIndexHandler)
|
||||||
var files []FileRecord
|
admin.GET("/delete/fr/:id", func(c *gin.Context) {
|
||||||
|
var record FileRecord
|
||||||
// Pagination parameters
|
if err := db.First(&record, "id = ?", c.Param("id")).Error; err == nil {
|
||||||
perPage := 20
|
performActualDeletion(&record)
|
||||||
page := 1
|
|
||||||
if p := c.Query("page"); p != "" {
|
|
||||||
if v, err := strconv.Atoi(p); err == nil && v > 0 {
|
|
||||||
page = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
c.Redirect(301, "/admin")
|
||||||
var total int64
|
|
||||||
db.Model(&FileRecord{}).Count(&total)
|
|
||||||
|
|
||||||
totalPages := int((total + int64(perPage) - 1) / int64(perPage)) // ceiling division
|
|
||||||
|
|
||||||
offset := (page - 1) * perPage
|
|
||||||
db.Order("created_at desc").Limit(perPage).Offset(offset).Find(&files)
|
|
||||||
|
|
||||||
c.HTML(200, "admin.html", gin.H{
|
|
||||||
"Files": files,
|
|
||||||
"Page": page,
|
|
||||||
"TotalPages": totalPages,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
admin.POST("/delete/:id", func(c *gin.Context) {
|
admin.GET("/delete/:id", func(c *gin.Context) {
|
||||||
var record FileRecord
|
var record FileRecord
|
||||||
if err := db.First(&record, "id = ?", c.Param("id")).Error; err == nil {
|
if err := db.First(&record, "id = ?", c.Param("id")).Error; err == nil {
|
||||||
performDeletion(&record)
|
performDeletion(&record)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type FileRecord struct {
|
|||||||
Path string `json:"-"`
|
Path string `json:"-"`
|
||||||
ExpiresAt time.Time `json:"expires_at"`
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
DeleteAfterDownload bool `json:"delete_after_download"`
|
DeleteAfterDownload bool `json:"delete_after_download"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
DownloadCount int `json:"download_count"`
|
DownloadCount int `json:"download_count"`
|
||||||
Deleted bool `json:"deleted"`
|
Deleted bool `json:"deleted"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|||||||
36
src/utils.go
36
src/utils.go
@@ -1,6 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -12,6 +15,19 @@ func performDeletion(r *FileRecord) {
|
|||||||
db.Save(r)
|
db.Save(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func performActualDeletion(r *FileRecord) {
|
||||||
|
|
||||||
|
folderPath := filepath.Join("uploads", r.ID)
|
||||||
|
err := os.RemoveAll(folderPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error deleting file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
db.Delete(r)
|
||||||
|
fmt.Println("Deleted file:", r.Filename)
|
||||||
|
}
|
||||||
|
|
||||||
func cleanupWorker() {
|
func cleanupWorker() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(10 * time.Minute)
|
time.Sleep(10 * time.Minute)
|
||||||
@@ -38,7 +54,7 @@ func authMiddleware() gin.HandlerFunc {
|
|||||||
|
|
||||||
tokenStr, err := c.Cookie("auth")
|
tokenStr, err := c.Cookie("auth")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Redirect(302, "/")
|
c.Redirect(302, "/login")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -48,7 +64,7 @@ func authMiddleware() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil || !token.Valid {
|
if err != nil || !token.Valid {
|
||||||
c.Redirect(302, "/")
|
c.Redirect(302, "/login")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -56,3 +72,19 @@ func authMiddleware() gin.HandlerFunc {
|
|||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func humanSize(size int64) string {
|
||||||
|
const unit = 1024
|
||||||
|
if size < unit {
|
||||||
|
return fmt.Sprintf("%d B", size)
|
||||||
|
}
|
||||||
|
div, exp := int64(unit), 0
|
||||||
|
for n := size / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.1f %cB",
|
||||||
|
float64(size)/float64(div),
|
||||||
|
"KMGTPE"[exp],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Filename</th>
|
<th>Filename</th>
|
||||||
|
<th>Size</th>
|
||||||
<th>Created</th>
|
<th>Created</th>
|
||||||
<th>Expires</th>
|
<th>Expires</th>
|
||||||
<th>Hits</th>
|
<th>Hits</th>
|
||||||
@@ -53,6 +54,7 @@
|
|||||||
<td class="font-mono">
|
<td class="font-mono">
|
||||||
<a href="/admin/download/{{.ID}}" target="_blank">{{.Filename}}</a>
|
<a href="/admin/download/{{.ID}}" target="_blank">{{.Filename}}</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td>{{humanSize .Size}}</td>
|
||||||
<td>{{.CreatedAt.Format "Jan 02, 2006 15:04"}}</td>
|
<td>{{.CreatedAt.Format "Jan 02, 2006 15:04"}}</td>
|
||||||
<td>{{.ExpiresAt.Format "Jan 02, 2006 15:04"}}</td>
|
<td>{{.ExpiresAt.Format "Jan 02, 2006 15:04"}}</td>
|
||||||
<td>{{.DownloadCount}}</td>
|
<td>{{.DownloadCount}}</td>
|
||||||
@@ -72,10 +74,13 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{if not .Deleted}}
|
{{if not .Deleted}}
|
||||||
<form action="/admin/delete/{{.ID}}" method="POST" onsubmit="return confirm('Kill this file?')">
|
<form action="/admin/delete/{{.ID}}" method="GET" onsubmit="return confirm('Kill this file?')">
|
||||||
<button type="submit">TERMINATE</button>
|
<button type="submit">TERMINATE</button>
|
||||||
</form>
|
</form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
<form action="/admin/delete/fr/{{.ID}}" method="GET" onsubmit="return confirm('Kill this file and the record?')">
|
||||||
|
<button type="submit">TERMINATE RECORD</button>
|
||||||
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
92
templates/deleted.html
Normal file
92
templates/deleted.html
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>File Deleted sucessfull</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<style>
|
||||||
|
* { border-radius: 0 !important; transition: none !important; }
|
||||||
|
body { font-family: sans-serif; background: #fff; color: #000; }
|
||||||
|
.box {
|
||||||
|
border: 3px solid #000;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 900;
|
||||||
|
border-bottom: 3px solid #000;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.subtitle {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
border: 2px solid #000;
|
||||||
|
background: #eee;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.ascii {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
border: 2px dashed #000;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen flex items-center justify-center p-4">
|
||||||
|
|
||||||
|
<div class="w-full max-w-[520px]">
|
||||||
|
|
||||||
|
<div class="box text-center">
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
FILE DELETED SUCESSFULL
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subtitle">
|
||||||
|
The file has been absolutely obliterated.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="ascii">-->
|
||||||
|
<!-- [ OK ] locating file...-->
|
||||||
|
<!-- [ OK ] emotionally detaching...-->
|
||||||
|
<!-- [ OK ] pressing the big red button...-->
|
||||||
|
<!-- [ OK ] file screaming detected...-->
|
||||||
|
<!-- [ OK ] scream ignored...-->
|
||||||
|
<!-- [ OK ] file is now gone forever™-->
|
||||||
|
|
||||||
|
<!-- (there is no undo)-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<!-- <div class="text-xs font-bold uppercase mb-4">-->
|
||||||
|
<!-- Congratulations. The electrons have been freed.-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<a href="/" class="button w-full">Pretend Nothing Happened</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
97
templates/error.html
Normal file
97
templates/error.html
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Nothing to see here</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
border: 2px solid #000;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
border: 2px solid #000;
|
||||||
|
background: #eee;
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:active {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 900;
|
||||||
|
border-bottom: 2px solid #000;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen flex items-center justify-center p-4">
|
||||||
|
|
||||||
|
<div class="w-full max-w-[493px] flex flex-col items-center">
|
||||||
|
|
||||||
|
<div class="box text-center">
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
NOTHING TO SEE HERE
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subtitle">
|
||||||
|
MOVE ALONG
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text">
|
||||||
|
This page is empty,<br>
|
||||||
|
unavailable, private,<br>
|
||||||
|
or intentionally left blank.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<a href="/" class="button w-full">GO BACK</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
88
templates/fileNotFound.html
Normal file
88
templates/fileNotFound.html
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>404 — File Not Found</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
border: 2px solid #000;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, .button {
|
||||||
|
border: 2px solid #000;
|
||||||
|
background: #eee;
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover, .button:hover {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active, .button:active {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-code {
|
||||||
|
font-size: 64px;
|
||||||
|
font-weight: 900;
|
||||||
|
border-bottom: 2px solid #000;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen flex items-center justify-center p-4">
|
||||||
|
|
||||||
|
<div class="w-full max-w-[493px] flex flex-col items-center">
|
||||||
|
|
||||||
|
<div class="box text-center">
|
||||||
|
|
||||||
|
<div class="error-code">404</div>
|
||||||
|
|
||||||
|
<div class="error-text mb-4">
|
||||||
|
FILE NOT FOUND 💀
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-xs mb-6 uppercase">
|
||||||
|
The requested file does not exist,<br>
|
||||||
|
has expired, or was destroyed,<br>or my db is fucked.
|
||||||
|
We'll never know :D
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<a href="/" class="button w-full">RETURN TO UPLOADER</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -335,6 +335,6 @@
|
|||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<a href="/login" class="fixed bottom-1 right-1 text-[10px] underline">SUDO</a>
|
<a href="/admin" class="fixed bottom-1 right-1 text-[10px] underline">SUDO</a>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user