🍣 Sushi
A fast, raw, tasty framework for simplifying basic web apps!
Quick Start
package main
import "git.sharkk.net/Sharkk/Sushi"
func main() {
app := sushi.New()
// Initialize sessions
sushi.InitSessions("sessions.json")
app.Get("/", func(ctx sushi.Ctx, params []any) {
ctx.SendHTML("<h1>Hello Sushi!</h1>")
})
app.Listen(":8080")
}
Routing
// Basic routes
app.Get("/users/:id", getUserHandler)
app.Post("/users", createUserHandler)
app.Put("/users/:id", updateUserHandler)
app.Delete("/users/:id", deleteUserHandler)
// Wildcards
app.Get("/files/*path", serveFilesHandler)
// Route groups
api := app.Group("/api/v1")
api.Get("/users", listUsersHandler)
api.Post("/users", createUserHandler)
Parameters
URL parameters are automatically converted to the appropriate type:
// Numeric parameters become integers
app.Get("/users/:id", func(ctx sushi.Ctx, params []any) {
userID := params[0].(int) // /users/123 -> 123
// ...
})
// String parameters stay strings
app.Get("/users/:name", func(ctx sushi.Ctx, params []any) {
name := params[0].(string) // /users/john -> "john"
// ...
})
// Mixed types
app.Get("/users/:id/posts/:slug", func(ctx sushi.Ctx, params []any) {
userID := params[0].(int) // 123
slug := params[1].(string) // "my-post"
// ...
})
Response Helpers
func myHandler(ctx sushi.Ctx, params []any) {
// JSON responses
ctx.SendJSON(map[string]string{"message": "success"})
// HTML responses
ctx.SendHTML("<h1>Welcome</h1>")
// Text responses
ctx.SendText("Plain text")
// Error responses
ctx.SendError(404, "Not Found")
// Redirects
ctx.Redirect("/login")
// Status only
ctx.SendStatus(204)
}
Middleware
// Custom middleware
func loggingMiddleware() sushi.Middleware {
return func(ctx sushi.Ctx, params []any, next func()) {
println("Request:", string(ctx.Method()), string(ctx.Path()))
next()
println("Status:", ctx.Response.StatusCode())
}
}
app.Use(loggingMiddleware())
// Group middleware
admin := app.Group("/admin")
admin.Use(auth.RequireAuth("/login"))
Authentication Workflow
1. Setup Password Hashing
import "git.sharkk.net/Sharkk/Sushi/password"
// Hash password for storage
hashedPassword := password.HashPassword("userpassword123")
// Verify password during login
isValid, err := password.VerifyPassword("userpassword123", hashedPassword)
2. User Structure
type User struct {
ID int `json:"id"`
Email string `json:"email"`
Username string `json:"username"`
Password string `json:"password"` // Store hashed password
}
// User lookup function for auth middleware
func getUserByID(userID int) any {
// Query your database for user by ID
// Return nil if not found
return &User{ID: userID, Email: "user@example.com"}
}
3. Session & Auth Middleware
import (
"git.sharkk.net/Sharkk/Sushi/session"
"git.sharkk.net/Sharkk/Sushi/auth"
)
func main() {
app := sushi.New()
// Initialize sessions
sushi.InitSessions("sessions.json")
// Add session middleware
app.Use(session.Middleware())
// Add auth middleware with user lookup
app.Use(auth.Middleware(getUserByID))
// Public routes
app.Get("/login", loginPageHandler)
app.Post("/login", loginHandler)
app.Post("/logout", logoutHandler)
// Protected routes
protected := app.Group("/dashboard")
protected.Use(auth.RequireAuth("/login"))
protected.Get("/", dashboardHandler)
}
4. Login Handler
func loginHandler(ctx sushi.Ctx, params []any) {
email := string(ctx.PostArgs().Peek("email"))
password := string(ctx.PostArgs().Peek("password"))
// Find user by email/username
user := findUserByEmail(email)
if user == nil {
ctx.SendError(401, "Invalid credentials")
return
}
// Verify password
isValid, err := password.VerifyPassword(password, user.Password)
if err != nil || !isValid {
ctx.SendError(401, "Invalid credentials")
return
}
// Log the user in
ctx.Login(user.ID, user)
ctx.Redirect("/dashboard")
}
5. Logout Handler
func logoutHandler(ctx sushi.Ctx, params []any) {
ctx.Logout()
ctx.Redirect("/")
}
6. Getting Current User
func dashboardHandler(ctx sushi.Ctx, params []any) {
user := ctx.GetCurrentUser().(*User)
html := fmt.Sprintf("<h1>Welcome, %s!</h1>", user.Username)
ctx.SendHTML(html)
}
CSRF Protection
import "git.sharkk.net/Sharkk/Sushi/csrf"
// Add CSRF middleware to forms
app.Use(csrf.Middleware())
// In your form template
func loginPageHandler(ctx sushi.Ctx, params []any) {
csrfField := csrf.CSRFHiddenField(ctx)
html := fmt.Sprintf(`
<form method="POST" action="/login">
%s
<input type="email" name="email" required>
<input type="password" name="password" required>
<button type="submit">Login</button>
</form>
`, csrfField)
ctx.SendHTML(html)
}
Static Files
// Serve static files
app.Get("/static/*path", sushi.Static("./public"))
// Serve single file
app.Get("/favicon.ico", sushi.StaticFile("./public/favicon.ico"))
// Embedded files
files := map[string][]byte{
"/style.css": cssData,
"/app.js": jsData,
}
app.Get("/assets/*path", sushi.StaticEmbed(files))
Sessions
func someHandler(ctx sushi.Ctx, params []any) {
sess := ctx.GetCurrentSession()
// Set session data
sess.Set("user_preference", "dark_mode")
// Get session data
if pref, exists := sess.Get("user_preference"); exists {
preference := pref.(string)
}
// Flash messages (one-time)
sess.SetFlash("success", "Profile updated!")
// Get flash message
message := sess.GetFlashMessage("success")
}
Server Configuration
app := sushi.New(sushi.ServerOptions{
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
MaxRequestBodySize: 10 * 1024 * 1024, // 10MB
})
// TLS
app.ListenTLS(":443", "cert.pem", "key.pem")
Complete Auth Example
package main
import (
"fmt"
sushi "git.sharkk.net/Sharkk/Sushi"
"git.sharkk.net/Sharkk/Sushi/auth"
"git.sharkk.net/Sharkk/Sushi/csrf"
"git.sharkk.net/Sharkk/Sushi/password"
"git.sharkk.net/Sharkk/Sushi/session"
)
type User struct {
ID int
Email string
Password string
}
var users = map[int]*User{
1: {ID: 1, Email: "admin@example.com", Password: password.HashPassword("admin123")},
}
func getUserByID(userID int) any {
return users[userID]
}
func findUserByEmail(email string) *User {
for _, user := range users {
if user.Email == email {
return user
}
}
return nil
}
func main() {
app := sushi.New()
sushi.InitSessions("sessions.json")
app.Use(session.Middleware())
app.Use(auth.Middleware(getUserByID))
// Public routes
app.Get("/", homeHandler)
app.Get("/login", loginPageHandler)
app.Post("/login", loginHandler)
// Protected routes
protected := app.Group("/dashboard")
protected.Use(auth.RequireAuth("/login"))
protected.Use(csrf.Middleware())
protected.Get("/", dashboardHandler)
protected.Post("/logout", logoutHandler)
app.Listen(":8080")
}
func homeHandler(ctx sushi.Ctx, params []any) {
if ctx.IsAuthenticated() {
ctx.Redirect("/dashboard")
return
}
ctx.SendHTML(`<a href="/login">Login</a>`)
}
func loginPageHandler(ctx sushi.Ctx, params []any) {
html := fmt.Sprintf(`
<form method="POST" action="/login">
%s
<input type="email" name="email" placeholder="Email" required><br>
<input type="password" name="password" placeholder="Password" required><br>
<button type="submit">Login</button>
</form>
`, csrf.HiddenField(ctx))
ctx.SendHTML(html)
}
func loginHandler(ctx sushi.Ctx, params []any) {
email := string(ctx.PostArgs().Peek("email"))
pass := string(ctx.PostArgs().Peek("password"))
user := findUserByEmail(email)
if user == nil {
ctx.SendError(401, "Invalid credentials")
return
}
if valid, _ := password.VerifyPassword(pass, user.Password); !valid {
ctx.SendError(401, "Invalid credentials")
return
}
ctx.Login(user.ID, user)
ctx.Redirect("/dashboard")
}
func dashboardHandler(ctx sushi.Ctx, params []any) {
user := ctx.GetCurrentUser().(*User)
html := fmt.Sprintf(`
<h1>Welcome, %s!</h1>
<form method="POST" action="/logout">
%s
<button type="submit">Logout</button>
</form>
`, user.Email, csrf.HiddenField(ctx))
ctx.SendHTML(html)
}
func logoutHandler(ctx sushi.Ctx, params []any) {
ctx.Logout()
ctx.Redirect("/")
}
Description
Languages
Go
100%