Web application framework built during school and free time.
Session feature uses sqlite as session storage, you'll need an gcc compiler to run your program.
go get github.com/robin-andreasson/fox
//Initialize root router
r := fox.Init()
/*
...routes and middlewares
*/
//Start server at port 3000
r.Listen(3000)
Map functions to url paths
r.Get("/", handler)
r.Post("/", handler)
r.Put("/", handler)
r.Delete("/", handler)
Add path prefixes to remove redundancy
auth := r.Group("auth")
// -> /auth/signin
auth.Post("/signin", handler)
// -> /auth/signup
auth.Post("/signup", handler)
Add variables in url paths, colon as delimiter
//Path variable named id
r.Get("/task/:id", getTask)
func getTask(c *fox.Context) error {
//get id
id := c.Params("id")
}
Regex pattern for specifying possible path variable values, semicolon as delimiter.
/*
Trigger handler only if page variable matches the regex ^\d+$
Fox wraps your regex statement between ^ and $
e.g. \d+ becomes ^\d+$
*/
r.Get("/book/:name/:page;\d+")
Fox parses incoming http bodies and maps them to an interface typed field in fox.Context called Body.
Multipart/form-data example:
/*
c.Body:
{
"Files": {
"file-name": {
"Data": []byte
"Filename": string
"Content-Type": string
}
}
}
*/
r.Post("/image", handler)
func handler(c *fox.Context) error {
//retrieve data from nested map interfaces with fox.Get
image := fox.Get[map[string]any](c.Body, "Files", "file-name")
data := fox.Get[[]byte](image, "Data")
filename := fox.Get[string](image, "Filename")
if err := os.WriteFile(filename, data, 777); err != nil {
return c.Status(fox.Status.InternalServerError)
}
return c.JSON(fox.Status.Created, map[string]string{"message": "successfully created image"})
}
A way to store information to be used across multiple pages.
/*
Initialize session options
*/
fox.Session(fox.SessionOptions{
Secret: "YOUR_SECRET",
TimeOut: 1000 * 60 * 60 * 24 * 30,
ClearProbability: 2.5,
Path: "./storage/session.sql",
Cookie: fox.CookieAttributes{
HttpOnly: true,
Secure: true,
SameSite: "Lax",
Path: "/",
ExpiresIn: 1000 * 60 * 60 * 24 * 30,
},
})
/*
Set session
*/
r.Post("/auth/signin", signin)
func signin(c *fox.Context) error {
//store information and create FOXSESSID cookie
c.SetSession(map[string]string{/*...data*/})
c.Status(fox.Status.Ok)
}
/*
Get session
*/
r.Post("/session", session)
func session(c *fox.Context) error {
c.Session// -> map[string]string{/*...data*/}
}
Allows the client to receive new jwt access token through the X-Fox-Access-Token header.
//initialize refresh options
fox.Refresh(fox.RefreshOptions{
//Found in X-Fox-Access-Token header
AccessToken: fox.TokenOptions{
Secret: "YOUR_ACCESS_TOKEN_SECRET",
Exp: 1000 * 60 * 5,
},
//Stored in FOXREFRESH cookie
RefreshToken: fox.TokenOptions{
Secret: "YOUR_REFRESH_TOKEN_SECRET",
Exp: 1000 * 60 * 60 * 24,
},
Cookie: fox.CookieAttributes{
HttpOnly: true,
Secure: true,
SameSite: "Lax",
Path: "/",
ExpiresIn: 1000 * 60 * 60 * 24,
},
RefreshFunction: handleRefresh,
})
/*
used to fetch the new access token data after it has expired.
refreshdata parameter is the data stored in refresh token.
*/
func handleRefresh(refreshdata any) (any, error) {
id := fox.Get[string](refreshdata, "id")
/*
fetch new access token data with the help of refresh token data
*/
result := fetch(id)
return result, nil
}
r.Post("/auth/signin", signin)
func signin(c *fox.Context) error {
//add non vulnerable data for fetching new access token data
refreshData := map[string]string{/*...data */}
accessData := map[string]string{/*...data */}
c.SetRefresh(accessData, refreshData)
c.Status(fox.Status.Ok)
}
/*
Get refresh
*/
r.Post("/refresh", session)
func session(c *fox.Context) error {
c.Refresh// -> map[string]string{/*...data*/}
}
Add functions to be called before the main handler
r.Post("/", middleware, handler)
func middleware(c *fox.Context) error {
//continue to the next middleware/handler in the stack
return c.Next()
}
Specify folder to be used to serve static files when the client endpoint requests them
/*
wd: /src/internal
*/
//Serve folder named public in /src/internal
r.Static("public")
//Serve folder named static in /src
r.Static("static", "../")