Fiber เป็นเฟรม เวิร์กเว็บ ที่ได้รับแรงบันดาลใจจาก Fasthttp ซึ่งเป็นเครื่องมือ HTTP ที่เร็วที่สุด สำหรับ GO ออกแบบมาเพื่อ บรรเทา สิ่งต่าง ๆ เพื่อการพัฒนา ที่รวดเร็ว ด้วย การจัดสรรหน่วยความจำ และ ประสิทธิภาพ ในใจ
Fiber V3 อยู่ในช่วงเบต้าและอยู่ระหว่างการพัฒนาที่ใช้งานอยู่ ในขณะที่มีคุณสมบัติใหม่ที่น่าตื่นเต้นโปรดทราบว่าอาจไม่มั่นคงสำหรับการใช้งานการผลิต เราขอแนะนำให้ยึดติดกับการเปิดตัวที่เสถียรล่าสุด (v2.x) สำหรับแอปพลิเคชันที่มีความสำคัญต่อภารกิจ หากคุณเลือกที่จะใช้ V3 ให้เตรียมพร้อมสำหรับข้อบกพร่องที่อาจเกิดขึ้นและทำลายการเปลี่ยนแปลง ตรวจสอบเอกสารอย่างเป็นทางการและบันทึกย่อสำหรับการอัปเดตและดำเนินการด้วยความระมัดระวัง การเข้ารหัสมีความสุข!
ไฟเบอร์ต้องการ GO เวอร์ชัน 1.23 หรือสูงกว่า ในการทำงาน หากคุณต้องการติดตั้งหรืออัพเกรดไปเยี่ยมชมหน้าดาวน์โหลดอย่างเป็นทางการ ในการเริ่มตั้งค่าโครงการของคุณให้สร้างไดเรกทอรีใหม่สำหรับโครงการของคุณและนำทางไป จากนั้นเริ่มต้นโครงการของคุณด้วยโมดูล GO โดยดำเนินการคำสั่งต่อไปนี้ในเทอร์มินัลของคุณ:
go mod init github.com/your/repoหากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับโมดูล GO และวิธีการทำงานคุณสามารถตรวจสอบโพสต์บล็อกการใช้งาน GO Modules
หลังจากตั้งค่าโครงการของคุณคุณสามารถติดตั้งไฟเบอร์ด้วยคำสั่ง go get :
go get -u github.com/gofiber/fiber/v3คำสั่งนี้ดึงแพ็คเกจไฟเบอร์และเพิ่มลงในการพึ่งพาของโครงการของคุณช่วยให้คุณเริ่มสร้างเว็บแอปพลิเคชันของคุณด้วยไฟเบอร์
การเริ่มต้นด้วยไฟเบอร์เป็นเรื่องง่าย นี่คือตัวอย่างพื้นฐานในการสร้างเว็บเซิร์ฟเวอร์ง่าย ๆ ที่ตอบสนองด้วย "สวัสดีโลก!" บนเส้นทางรูท ตัวอย่างนี้แสดงให้เห็นถึงการเริ่มต้นแอพไฟเบอร์ใหม่ตั้งค่าเส้นทางและเริ่มต้นเซิร์ฟเวอร์
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func main () {
// Initialize a new Fiber app
app := fiber . New ()
// Define a route for the GET method on the root path '/'
app . Get ( "/" , func ( c fiber. Ctx ) error {
// Send a string response to the client
return c . SendString ( "Hello, World !" )
})
// Start the server on port 3000
log . Fatal ( app . Listen ( ":3000" ))
} เซิร์ฟเวอร์ง่าย ๆ นี้ง่ายต่อการตั้งค่าและเรียกใช้ มันแนะนำแนวคิดหลักของไฟเบอร์: การเริ่มต้นแอปการกำหนดเส้นทางและการเริ่มต้นเซิร์ฟเวอร์ เพียงเรียกใช้โปรแกรม GO นี้และเยี่ยมชม http://localhost:3000 ในเบราว์เซอร์ของคุณเพื่อดูข้อความ
ไฟเบอร์ได้รับการปรับให้เหมาะสมสำหรับ ประสิทธิภาพสูง ค่าความหมายที่ส่งคืนจาก fiber.ctx จะ ไม่ เปลี่ยนรูปโดยค่าเริ่มต้นและ จะ ถูกนำกลับมาใช้ใหม่ในคำขอ ตามกฎของหัวแม่มือคุณ ต้อง ใช้ค่าบริบทภายในตัวจัดการเท่านั้นและ ต้องไม่ เก็บข้อมูลอ้างอิงใด ๆ เมื่อคุณกลับมาจากตัวจัดการค่าใด ๆ ที่ได้จากบริบทจะถูกนำกลับมาใช้ใหม่ในคำขอในอนาคต เยี่ยมชมเอกสารของเราเพื่อเรียนรู้เพิ่มเติม
การทดสอบเหล่านี้ดำเนินการโดย TechEmpower และ Go Web หากคุณต้องการดูผลลัพธ์ทั้งหมดกรุณาเยี่ยมชมวิกิของเรา
โกเฟอร์ใหม่ที่ทำให้สวิตช์จาก Node.js ไปต้องจัดการกับช่วงการเรียนรู้ก่อนที่พวกเขาจะสามารถเริ่มสร้างเว็บแอปพลิเคชันหรือไมโครไซต์ ไฟเบอร์เป็น กรอบเว็บ ถูกสร้างขึ้นด้วยแนวคิดเรื่อง ความเรียบง่าย และติดตาม วิธี UNIX เพื่อให้โกเฟอร์ใหม่สามารถเข้าสู่โลกแห่งการเดินทางด้วยการต้อนรับที่อบอุ่นและน่าเชื่อถือ
ไฟเบอร์ได้ รับแรงบันดาลใจ จาก Express ซึ่งเป็นเฟรมเวิร์กเว็บที่ได้รับความนิยมมากที่สุดบนอินเทอร์เน็ต เรารวม ความสะดวก ในการแสดงและ ประสิทธิภาพ ของ GO หากคุณเคยใช้แอปพลิเคชันเว็บใน node.js ( ใช้ด่วนหรือคล้ายกัน ) จากนั้นวิธีการและหลักการมากมายจะดูเป็น เรื่องธรรมดามาก สำหรับคุณ
เรา ฟัง ผู้ใช้ของเราในประเด็นช่องทาง Discord และทั่วอินเทอร์เน็ต เพื่อสร้างกรอบเว็บ ที่รวดเร็ว ยืดหยุ่น และ เป็นมิตร สำหรับงาน ใด ๆ ที่กำหนดเวลา และ ทักษะ นักพัฒนา! เช่นเดียวกับ Express ในโลก JavaScript
รายการด้านล่างเป็นตัวอย่างทั่วไปบางส่วน หากคุณต้องการดูตัวอย่างรหัสเพิ่มเติมโปรดไปที่ที่เก็บสูตรอาหารของเราหรือเยี่ยมชมเอกสาร API ที่โฮสต์ของเรา
package main
import (
"fmt"
"log"
"github.com/gofiber/fiber/v3"
)
func main () {
app := fiber . New ()
// GET /api/register
app . Get ( "/api/*" , func ( c fiber. Ctx ) error {
msg := fmt . Sprintf ( " %s" , c . Params ( "*" ))
return c . SendString ( msg ) // => register
})
// GET /flights/LAX-SFO
app . Get ( "/flights/:from-:to" , func ( c fiber. Ctx ) error {
msg := fmt . Sprintf ( "? From: %s, To: %s" , c . Params ( "from" ), c . Params ( "to" ))
return c . SendString ( msg ) // => ? From: LAX, To: SFO
})
// GET /dictionary.txt
app . Get ( "/:file.:ext" , func ( c fiber. Ctx ) error {
msg := fmt . Sprintf ( "? %s.%s" , c . Params ( "file" ), c . Params ( "ext" ))
return c . SendString ( msg ) // => ? dictionary.txt
})
// GET /john/75
app . Get ( "/:name/:age/:gender?" , func ( c fiber. Ctx ) error {
msg := fmt . Sprintf ( "? %s is %s years old" , c . Params ( "name" ), c . Params ( "age" ))
return c . SendString ( msg ) // => ? john is 75 years old
})
// GET /john
app . Get ( "/:name" , func ( c fiber. Ctx ) error {
msg := fmt . Sprintf ( "Hello, %s !" , c . Params ( "name" ))
return c . SendString ( msg ) // => Hello john !
})
log . Fatal ( app . Listen ( ":3000" ))
} package main
import (
"encoding/json"
"fmt"
"log"
"github.com/gofiber/fiber/v3"
)
func main () {
app := fiber . New ()
app . Get ( "/api/*" , func ( c fiber. Ctx ) error {
msg := fmt . Sprintf ( " %s" , c . Params ( "*" ))
return c . SendString ( msg ) // => register
}). Name ( "api" )
route := app . GetRoute ( "api" )
data , _ := json . MarshalIndent ( route , "" , " " )
fmt . Println ( string ( data ))
// Prints:
// {
// "method": "GET",
// "name": "api",
// "path": "/api/*",
// "params": [
// "*1"
// ]
// }
log . Fatal ( app . Listen ( ":3000" ))
} package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/static"
)
func main () {
app := fiber . New ()
// Serve static files from the "./public" directory
app . Get ( "/*" , static . New ( "./public" ))
// => http://localhost:3000/js/script.js
// => http://localhost:3000/css/style.css
app . Get ( "/prefix*" , static . New ( "./public" ))
// => http://localhost:3000/prefix/js/script.js
// => http://localhost:3000/prefix/css/style.css
// Serve a single file for any unmatched routes
app . Get ( "*" , static . New ( "./public/index.html" ))
// => http://localhost:3000/any/path/shows/index.html
log . Fatal ( app . Listen ( ":3000" ))
} package main
import (
"fmt"
"log"
"github.com/gofiber/fiber/v3"
)
func main () {
app := fiber . New ()
// Middleware that matches any route
app . Use ( func ( c fiber. Ctx ) error {
fmt . Println ( "? First handler" )
return c . Next ()
})
// Middleware that matches all routes starting with /api
app . Use ( "/api" , func ( c fiber. Ctx ) error {
fmt . Println ( "? Second handler" )
return c . Next ()
})
// GET /api/list
app . Get ( "/api/list" , func ( c fiber. Ctx ) error {
fmt . Println ( "? Last handler" )
return c . SendString ( "Hello, World !" )
})
log . Fatal ( app . Listen ( ":3000" ))
}การกำหนดค่าเอ็นจินเรนเดอร์
ค่าเริ่มต้นไฟเบอร์เป็น HTML/เทมเพลตเมื่อไม่มีการตั้งค่าเอ็นจิ้นมุมมอง
หากคุณต้องการเรียกใช้งานส่วนหนึ่งหรือใช้เอ็นจิ้นอื่นเช่นอำพันแฮนด์บาร์หนวดหรือปั๊ก ฯลฯ ลองดูแพ็คเกจเทมเพลตของเราที่รองรับเอ็นจิ้นหลายมุมมอง
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/template/pug"
)
func main () {
// Initialize a new Fiber app with Pug template engine
app := fiber . New (fiber. Config {
Views : pug . New ( "./views" , ".pug" ),
})
// Define a route that renders the "home.pug" template
app . Get ( "/" , func ( c fiber. Ctx ) error {
return c . Render ( "home" , fiber. Map {
"title" : "Homepage" ,
"year" : 1999 ,
})
})
log . Fatal ( app . Listen ( ":3000" ))
}กลุ่ม
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func middleware ( c fiber. Ctx ) error {
log . Println ( "Middleware executed" )
return c . Next ()
}
func handler ( c fiber. Ctx ) error {
return c . SendString ( "Handler response" )
}
func main () {
app := fiber . New ()
// Root API group with middleware
api := app . Group ( "/api" , middleware ) // /api
// API v1 routes
v1 := api . Group ( "/v1" , middleware ) // /api/v1
v1 . Get ( "/list" , handler ) // /api/v1/list
v1 . Get ( "/user" , handler ) // /api/v1/user
// API v2 routes
v2 := api . Group ( "/v2" , middleware ) // /api/v2
v2 . Get ( "/list" , handler ) // /api/v2/list
v2 . Get ( "/user" , handler ) // /api/v2/user
log . Fatal ( app . Listen ( ":3000" ))
}คนตัดไม้
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/logger"
)
func main () {
app := fiber . New ()
// Use Logger middleware
app . Use ( logger . New ())
// Define routes
app . Get ( "/" , func ( c fiber. Ctx ) error {
return c . SendString ( "Hello, Logger!" )
})
log . Fatal ( app . Listen ( ":3000" ))
}ไม้กางเขน
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/cors"
)
func main () {
app := fiber . New ()
// Use CORS middleware with default settings
app . Use ( cors . New ())
// Define routes
app . Get ( "/" , func ( c fiber. Ctx ) error {
return c . SendString ( "CORS enabled!" )
})
log . Fatal ( app . Listen ( ":3000" ))
} ตรวจสอบ cors โดยผ่านโดเมนใด ๆ ในส่วนหัว Origin :
curl -H " Origin: http://example.com " --verbose http://localhost:3000วิธี HTTP
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func main () {
app := fiber . New ()
// Define routes
app . Get ( "/" , static . New ( "./public" ))
app . Get ( "/demo" , func ( c fiber. Ctx ) error {
return c . SendString ( "This is a demo page!" )
})
app . Post ( "/register" , func ( c fiber. Ctx ) error {
return c . SendString ( "Registration successful!" )
})
// Middleware to handle 404 Not Found
app . Use ( func ( c fiber. Ctx ) error {
return c . SendStatus ( fiber . StatusNotFound ) // => 404 "Not Found"
})
log . Fatal ( app . Listen ( ":3000" ))
}JSON
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main () {
app := fiber . New ()
// Route that returns a JSON object
app . Get ( "/user" , func ( c fiber. Ctx ) error {
return c . JSON ( & User { "John" , 20 })
// => {"name":"John", "age":20}
})
// Route that returns a JSON map
app . Get ( "/json" , func ( c fiber. Ctx ) error {
return c . JSON (fiber. Map {
"success" : true ,
"message" : "Hi John!" ,
})
// => {"success":true, "message":"Hi John!"}
})
log . Fatal ( app . Listen ( ":3000" ))
}websocket
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/websocket"
)
func main () {
app := fiber . New ()
// WebSocket route
app . Get ( "/ws" , websocket . New ( func ( c * websocket. Conn ) {
defer c . Close ()
for {
// Read message from client
mt , msg , err := c . ReadMessage ()
if err != nil {
log . Println ( "read:" , err )
break
}
log . Printf ( "recv: %s" , msg )
// Write message back to client
err = c . WriteMessage ( mt , msg )
if err != nil {
log . Println ( "write:" , err )
break
}
}
}))
log . Fatal ( app . Listen ( ":3000" ))
// Connect via WebSocket at ws://localhost:3000/ws
}ข้อมูลเพิ่มเติม
package main
import (
"bufio"
"fmt"
"log"
"time"
"github.com/gofiber/fiber/v3"
"github.com/valyala/fasthttp"
)
func main () {
app := fiber . New ()
// Server-Sent Events route
app . Get ( "/sse" , func ( c fiber. Ctx ) error {
c . Set ( "Content-Type" , "text/event-stream" )
c . Set ( "Cache-Control" , "no-cache" )
c . Set ( "Connection" , "keep-alive" )
c . Set ( "Transfer-Encoding" , "chunked" )
c . Context (). SetBodyStreamWriter ( func ( w * bufio. Writer ) {
var i int
for {
i ++
msg := fmt . Sprintf ( "%d - the time is %v" , i , time . Now ())
fmt . Fprintf ( w , "data: Message: %s n n " , msg )
fmt . Println ( msg )
w . Flush ()
time . Sleep ( 5 * time . Second )
}
})
return nil
})
log . Fatal ( app . Listen ( ":3000" ))
}ฟื้นตัว
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/recover"
)
func main () {
app := fiber . New ()
// Use Recover middleware to handle panics gracefully
app . Use ( recover . New ())
// Route that intentionally panics
app . Get ( "/" , func ( c fiber. Ctx ) error {
panic ( "normally this would crash your app" )
})
log . Fatal ( app . Listen ( ":3000" ))
}การกำหนดค่า
package main
import (
"log"
"github.com/gofiber/fiber/v3"
)
func main () {
app := fiber . New (fiber. Config {
// Configure trusted proxies - WARNING: Only trust proxies you control
// Using TrustProxy: true with unrestricted IPs can lead to IP spoofing
TrustProxy : true ,
TrustProxyConfig : fiber. TrustProxyConfig {
Proxies : [] string { "10.0.0.0/8" , "172.16.0.0/12" }, // Example: Internal network ranges only
},
ProxyHeader : fiber . HeaderXForwardedFor ,
})
// Define routes
app . Get ( "/" , func ( c fiber. Ctx ) error {
return c . SendString ( "Trusted Proxy Configured!" )
})
log . Fatal ( app . Listen ( ":3000" ))
}นี่คือรายการของมิดเดิลแวร์ที่รวมอยู่ในเฟรมเวิร์กไฟเบอร์
| มิดเดิลแวร์ | คำอธิบาย |
|---|---|
| อะแดปเตอร์ | ตัวแปลงสำหรับตัวจัดการ NET/HTTP เป็น/จากตัวจัดการคำขอไฟเบอร์ |
| ขั้นพื้นฐาน | ให้การรับรองความถูกต้องพื้นฐาน HTTP มันเรียกตัวจัดการถัดไปสำหรับข้อมูลรับรองที่ถูกต้องและ 401 โดยไม่ได้รับอนุญาตสำหรับข้อมูลรับรองที่หายไปหรือไม่ถูกต้อง |
| แคช | การสกัดกั้นและแคชการตอบสนอง HTTP |
| บีบอัด | มิดเดิลแวร์การบีบอัดสำหรับไฟเบอร์พร้อมการสนับสนุนสำหรับ deflate , gzip , brotli และ zstd |
| ไม้กางเขน | เปิดใช้งานการแบ่งปันทรัพยากรข้ามแหล่งกำเนิด (CORS) ด้วยตัวเลือกต่าง ๆ |
| CSRF | ป้องกันการหาประโยชน์จาก CSRF |
| ต้น | เพิ่มการสนับสนุนสำหรับคุณลักษณะข้อมูลต้นของ TLS 1.3 ("0-RTT") |
| Encryptcookie | เข้ารหัสมิดเดิลแวร์ที่เข้ารหัสค่าคุกกี้ |
| envvar | เปิดเผยตัวแปรสภาพแวดล้อมด้วยการกำหนดค่าเสริม |
| etag | อนุญาตให้แคชมีประสิทธิภาพมากขึ้นและบันทึกแบนด์วิดท์เนื่องจากเว็บเซิร์ฟเวอร์ไม่จำเป็นต้องส่งการตอบกลับอย่างเต็มรูปแบบหากเนื้อหาไม่เปลี่ยนแปลง |
| expvar | ให้บริการผ่านตัวแปรเปิดเผยรันไทม์เซิร์ฟเวอร์ HTTP ในรูปแบบ JSON |
| ความคิด | ละเว้น favicon จากบันทึกหรือให้บริการจากหน่วยความจำหากมีการจัดเตรียมเส้นทางไฟล์ |
| HealthCheck | โพรบและความพร้อมสำหรับเส้นใย |
| หมวกนิรภัย | ช่วยรักษาความปลอดภัยแอพของคุณโดยการตั้งส่วนหัว HTTP ต่างๆ |
| ความไม่แน่นอน | อนุญาตให้ APIs ที่ทนต่อความผิดพลาดซึ่งคำขอที่ซ้ำกันไม่ได้ทำให้การกระทำเดียวกันดำเนินการหลายครั้งบนฝั่งเซิร์ฟเวอร์ |
| คีย์ | เพิ่มการสนับสนุนสำหรับการรับรองความถูกต้องตามคีย์ |
| ตัว จำกัด | เพิ่มการรองรับการ จำกัด อัตราให้กับไฟเบอร์ ใช้เพื่อ จำกัด การร้องขอซ้ำไปยัง API สาธารณะและ/หรือจุดสิ้นสุดเช่นการรีเซ็ตรหัสผ่าน |
| คนตัดไม้ | http คำขอ/ตัวบันทึกการตอบกลับ |
| pprof | ให้บริการข้อมูลการทำโปรไฟล์รันไทม์ในรูปแบบ PPROF |
| พร็อกซี | ช่วยให้คุณสามารถร้องขอพร็อกซีไปยังเซิร์ฟเวอร์หลายเครื่อง |
| ฟื้นตัว | กู้คืนจากความตื่นตระหนกได้ทุกที่ในห่วงโซ่สแต็กและจัดการการควบคุมไปยัง Error Handler ส่วนกลาง |
| เปลี่ยนเส้นทาง | เปลี่ยนเส้นทางมิดเดิลแวร์ |
| การร้องขอ | เพิ่มรหัสคำขอในทุกคำขอ |
| เขียนใหม่ | เขียนเส้นทาง URL ใหม่ตามกฎที่ให้ไว้ มันจะมีประโยชน์สำหรับความเข้ากันได้ย้อนหลังหรือเพียงแค่สร้างลิงก์ที่สะอาดและอธิบายมากขึ้น |
| การประชุม | มิดเดิลแวร์เซสชัน หมายเหตุ: มิดเดิลแวร์นี้ใช้แพ็คเกจที่เก็บข้อมูลของเรา |
| ข้าม | ข้ามมิดเดิลแวร์ที่ข้ามตัวจัดการที่ห่อหุ้มไว้หากเพรดิเคตเป็นจริง |
| คงที่ | มิดเดิลแวร์แบบคงที่สำหรับไฟเบอร์ที่ให้บริการไฟล์คงที่เช่น รูปภาพ , CSS และ JavaScript |
| การหมดเวลา | เพิ่มเวลาสูงสุดสำหรับการร้องขอและส่งต่อไปยังข้อผิดพลาดหากเกิน |
รายการโมดูลมิดเดิลแวร์ที่โฮสต์ภายนอกและดูแลโดยทีมไฟเบอร์
| มิดเดิลแวร์ | คำอธิบาย |
|---|---|
| การควบคุม | บุคคลที่สาม |
| พื้นที่จัดเก็บ | ไดรเวอร์จัดเก็บข้อมูลล่วงหน้าที่ใช้อินเทอร์เฟซจัดเก็บข้อมูลที่ออกแบบมาเพื่อใช้กับเส้นใยกลางต่างๆ |
| เทมเพลต | แพ็คเกจนี้มีเครื่องยนต์เทมเพลต 9 เครื่องที่สามารถใช้กับไฟเบอร์ v3 ต้องใช้เวอร์ชัน 1.23 หรือสูงกว่า |
สำหรับบทความเพิ่มเติม Middlewares ตัวอย่างหรือเครื่องมือตรวจสอบรายการที่ยอดเยี่ยมของเรา
หากคุณต้องการพูด ขอบคุณ และ/หรือสนับสนุนการพัฒนาของ Fiber :
เพื่อให้แน่ใจว่าการมีส่วนร่วมของคุณพร้อมสำหรับการร้องขอการดึงโปรดใช้คำสั่ง Makefile ต่อไปนี้ เครื่องมือเหล่านี้ช่วยรักษาคุณภาพของรหัสและความสอดคล้อง
เรียกใช้คำสั่งเหล่านี้เพื่อให้แน่ใจว่ารหัสของคุณปฏิบัติตามมาตรฐานโครงการและแนวทางปฏิบัติที่ดีที่สุด
ไฟเบอร์เป็นโครงการโอเพนซอร์ซที่ดำเนินการในการบริจาคเพื่อชำระค่าใช้จ่ายเช่นชื่อโดเมนของเรา, gitbook, netlify และโฮสติ้งที่ไม่มีเซิร์ฟเวอร์ หากคุณต้องการสนับสนุนไฟเบอร์คุณสามารถ ซื้อกาแฟได้ที่นี่
| ผู้ใช้ | การบริจาค | |
|---|---|---|
| @destari | ☕ x 10 | |
| @dembygenesis | ☕ x 5 | |
| @thomasvvugt | ☕ x 5 | |
| @hendratommy | ☕ x 5 | |
| @ekaputra07 | ☕ x 5 | |
| @jorgefuertes | ☕ x 5 | |
| @candidosales | ☕ x 5 | |
| @l0nax | ☕ x 3 | |
| @Bihe | ☕ x 3 | |
| @JustDave | ☕ x 3 | |
| @koddr | ☕ x 1 | |
| @lapolinar | ☕ x 1 | |
| @diegowifi | ☕ x 1 | |
| @ssimk0 | ☕ x 1 | |
| @raymayemir | ☕ x 1 | |
| @Melkorm | ☕ x 1 | |
| @marvinjwendt | ☕ x 1 | |
| @toishy | ☕ x 1 |
ลิขสิทธิ์ (C) 2019- ปัจจุบัน Fenny และผู้สนับสนุน Fiber เป็นซอฟต์แวร์ฟรีและโอเพ่นซอร์สที่ได้รับอนุญาตภายใต้ใบอนุญาต MIT โลโก้อย่างเป็นทางการถูกสร้างขึ้นโดย Vic Shóstakและจัดจำหน่ายภายใต้ใบอนุญาตครีเอทีฟคอมมอนส์ (CC BY-SA 4.0 International)