mirror of
https://github.com/bumi/lntip
synced 2025-06-16 09:45:35 +00:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
70028e4264 | |||
|
47efbfcc96 | ||
b9b2ce8d6b | |||
|
f604ca21ce | ||
530c7c0942 | |||
|
23a134b28e | ||
c6dc9308c3 | |||
|
897217995a | ||
a21e8b96b7 | |||
a383a747ef | |||
|
9ceea9e6bf | ||
d42c12be9b | |||
|
53253eba76 | ||
a3675ec746 | |||
3c8a76a1c1 | |||
f6d089c194 |
51
README.md
51
README.md
@ -5,7 +5,7 @@ LnMe is a personal Bitcoin Lightning payment page/widget and self-hosted [Lightn
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
**See it in action: [ln.michaelbumann.com](https://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com**
|
**See it in action: [ln.michaelbumann.com](http://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com**
|
||||||
|
|
||||||
LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node (currently LND is supported).
|
LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node (currently LND is supported).
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ LnMe is one [simple executable](https://github.com/bumi/lnme/releases) file that
|
|||||||
- [x] [JavaScript widget](#javascript-widget-integration) for existing websites
|
- [x] [JavaScript widget](#javascript-widget-integration) for existing websites
|
||||||
- [x] [Invoice API](https://github.com/bumi/lnme/wiki/API) - simple REST API to create LN invoices from existing JS code
|
- [x] [Invoice API](https://github.com/bumi/lnme/wiki/API) - simple REST API to create LN invoices from existing JS code
|
||||||
- [x] [LNURL-pay](https://github.com/fiatjaf/lnurl-rfc/blob/luds/06.md) support
|
- [x] [LNURL-pay](https://github.com/fiatjaf/lnurl-rfc/blob/luds/06.md) support
|
||||||
|
- [x] [LNURL-pay comment](https://github.com/fiatjaf/lnurl-rfc/blob/luds/12.md) support
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -62,10 +63,12 @@ Instead of the path to the macaroon and cert files you can also provide the hex
|
|||||||
#### Other configuration
|
#### Other configuration
|
||||||
|
|
||||||
- `static-path`: Path to a folder that you want to serve with LnMe (e.g. /home/bitcoin/lnme/website). Use this if you want to customize your ⚡website. default: disabled
|
- `static-path`: Path to a folder that you want to serve with LnMe (e.g. /home/bitcoin/lnme/website). Use this if you want to customize your ⚡website. default: disabled
|
||||||
|
- `lnurlp-comment-allowed`: Allowed length of LNURL-pay comments, maximum around [~2000 characters](https://stackoverflow.com/a/417184). (default: 210)
|
||||||
- `disable-website`: Disable the default LnMe website. Disable the website if you only want to embed the LnMe widget on your existing website.
|
- `disable-website`: Disable the default LnMe website. Disable the website if you only want to embed the LnMe widget on your existing website.
|
||||||
- `disable-cors`: Disable CORS headers. (default: false)
|
- `disable-cors`: Disable CORS headers. (default: false)
|
||||||
- `disable-ln-address`: Disable [Lightning Address](https://lightningaddress.com/) handling.
|
- `disable-ln-address`: Disable [Lightning Address](https://lightningaddress.com/) handling.
|
||||||
- `port`: Port to listen on. (default: 1323)
|
- `port`: Port to listen on. (default: 1323)
|
||||||
|
- `listen`: IP and port to listen to. Supersedes `port`. (default: :1323).
|
||||||
- `request-limit`: Limit the allowed requests per second. (default: 5)
|
- `request-limit`: Limit the allowed requests per second. (default: 5)
|
||||||
|
|
||||||
Depending on your deployment needs LnMe can be configured using the following options:
|
Depending on your deployment needs LnMe can be configured using the following options:
|
||||||
@ -121,6 +124,7 @@ The TLS cert is located in the lnd directory:
|
|||||||
|
|
||||||
- ~/umbrel/lnd/tls.cert on Umbrel
|
- ~/umbrel/lnd/tls.cert on Umbrel
|
||||||
- /mnt/hdd/lnd/tls.cert on Raspiblitz
|
- /mnt/hdd/lnd/tls.cert on Raspiblitz
|
||||||
|
- /embassy-data/package-data/volumes/lnd/data/main/tls.cert on Start9 Embassy
|
||||||
- Can also be located in ~/.lnd
|
- Can also be located in ~/.lnd
|
||||||
|
|
||||||
You should find the macaroon files in the LND data dir (e.g. ~.lnd/data/chain/bitcoin/mainnet/) or see "LND Permissions" how to create a new one.
|
You should find the macaroon files in the LND data dir (e.g. ~.lnd/data/chain/bitcoin/mainnet/) or see "LND Permissions" how to create a new one.
|
||||||
@ -138,7 +142,41 @@ This buildpack can be disabled and removed if not needed or desired, through the
|
|||||||
|
|
||||||
Lastly, using the Heroku deployment, you can link the app to your own domain by following the directions here: https://help.heroku.com/MTG1BIA7/how-do-i-connect-a-domain-to-my-heroku-app
|
Lastly, using the Heroku deployment, you can link the app to your own domain by following the directions here: https://help.heroku.com/MTG1BIA7/how-do-i-connect-a-domain-to-my-heroku-app
|
||||||
|
|
||||||
### Deployment Notes
|
|
||||||
|
### Fly.io
|
||||||
|
|
||||||
|
#### 0. Clone the repo
|
||||||
|
|
||||||
|
$ git clone https://github.com/bumi/lnme.git
|
||||||
|
$ cd lnme
|
||||||
|
|
||||||
|
#### 1. Create a new app
|
||||||
|
|
||||||
|
$ flyctl launch --generate-name // or set a custom app name: flyctl launch --name lnme-test-1
|
||||||
|
|
||||||
|
You will be asked a few things:
|
||||||
|
|
||||||
|
* Copy the configuration to the new app
|
||||||
|
* You do NOT need to create a Postgresql Database
|
||||||
|
* Do NOT deploy it directly, we first need to set some configs
|
||||||
|
|
||||||
|
#### 2. Set the configuration using environment variables:
|
||||||
|
|
||||||
|
The LND config variablse are required. Others are optional:
|
||||||
|
|
||||||
|
$ flyctl secrets set LNME_LND_ADDRESS="xxx.xxx.xxx.xxx:10009" LNME_LND_CERT=xxx LNME_LND_MACAROON=xxx
|
||||||
|
$ flyctl secrets set DISABLE_WEBSITE=1 // etc.
|
||||||
|
|
||||||
|
#### 3. Launch the app:
|
||||||
|
|
||||||
|
$ flyctl deploy
|
||||||
|
|
||||||
|
#### 4.Configure your domain
|
||||||
|
|
||||||
|
To configure a custom domain check the [fly.io guides](https://fly.io/docs/app-guides/custom-domains-with-fly/)
|
||||||
|
|
||||||
|
|
||||||
|
### Custom deployment notes
|
||||||
|
|
||||||
To run LnMe as systemd service have a look at the [systemd service example config](https://github.com/bumi/lnme/blob/master/examples/lnme.service)
|
To run LnMe as systemd service have a look at the [systemd service example config](https://github.com/bumi/lnme/blob/master/examples/lnme.service)
|
||||||
|
|
||||||
@ -171,6 +209,8 @@ if you got the Lightning Address enabled you also get a LNURL-pay URL:
|
|||||||
|
|
||||||
https://`{your domain}/lnurlp/{anything}`
|
https://`{your domain}/lnurlp/{anything}`
|
||||||
|
|
||||||
|
If you need an bech32 encoded version you can use this online tool: [https://lnurl.fiatjaf.com/codec/](https://lnurl.fiatjaf.com/codec/)
|
||||||
|
|
||||||
### Customize your ⚡ website
|
### Customize your ⚡ website
|
||||||
|
|
||||||
LnMe comes with a default website but you can easily configure and build your own using the the LnMe JavaScript widget or JSON API.
|
LnMe comes with a default website but you can easily configure and build your own using the the LnMe JavaScript widget or JSON API.
|
||||||
@ -181,6 +221,13 @@ Take a look at the [embedded default website](https://github.com/bumi/lnme/blob/
|
|||||||
2. Create your index.html
|
2. Create your index.html
|
||||||
3. Run lnme: `lnme --static-path=/home/satoshi/my-ln-page
|
3. Run lnme: `lnme --static-path=/home/satoshi/my-ln-page
|
||||||
|
|
||||||
|
### Usage with 21 Payment Widgets
|
||||||
|
|
||||||
|
[widgets.twentyuno.net](https://widgets.twentyuno.net/) is a beautiful embeddable payment widget for any existing website.
|
||||||
|
You can use your LnMe instance with the widget by using your [LnMe LNURL](https://github.com/bumi/lnme#lnurl) with the widget.
|
||||||
|
|
||||||
|
Use your bech32 encoded [LNURL](https://github.com/bumi/lnme#lnurl) as `Receiver` in the [widget configuration](https://widgets.twentyuno.net/get-started)
|
||||||
|
|
||||||
### JavaScript Widget integration
|
### JavaScript Widget integration
|
||||||
|
|
||||||
You can integrate the LnMe widget in your existing website.
|
You can integrate the LnMe widget in your existing website.
|
||||||
|
@ -120,15 +120,11 @@ class LnMe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
payWithWebln() {
|
payWithWebln() {
|
||||||
if (!webln.isEnabled) {
|
webln.enable().then((weblnResponse) => {
|
||||||
webln.enable().then((weblnResponse) => {
|
|
||||||
return webln.sendPayment(this.invoice.payment_request);
|
|
||||||
}).catch((e) => {
|
|
||||||
return this.showPaymentRequest();
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return webln.sendPayment(this.invoice.payment_request);
|
return webln.sendPayment(this.invoice.payment_request);
|
||||||
}
|
}).catch((e) => {
|
||||||
|
return this.showPaymentRequest();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
populatePaymentRequest() {
|
populatePaymentRequest() {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta property="og:image" content="/lnme/icon.svg">
|
<meta property="og:image" content="/lnme/icon.svg">
|
||||||
|
|
||||||
<title>Send me some Sats</title>
|
<title>Send me some sats</title>
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -118,7 +118,7 @@
|
|||||||
<div class="form" id="form">
|
<div class="form" id="form">
|
||||||
<p>
|
<p>
|
||||||
Send me<br>
|
Send me<br>
|
||||||
<input type="number" placeholder="10000" class="amount" id="amount" autofocus="true" autocomplete="off" min="100"> Sats
|
<input type="number" placeholder="10000" class="amount" id="amount" autofocus="true" autocomplete="off" min="100"> sats
|
||||||
<br>
|
<br>
|
||||||
for
|
for
|
||||||
<br>
|
<br>
|
||||||
@ -159,7 +159,7 @@
|
|||||||
}
|
}
|
||||||
var siteDescription = document.querySelector('head > meta[property="og:description"]');
|
var siteDescription = document.querySelector('head > meta[property="og:description"]');
|
||||||
if (siteDescription && !siteDescription.content) {
|
if (siteDescription && !siteDescription.content) {
|
||||||
siteDescription.content = "Sats for " + window.location.host;
|
siteDescription.content = "sats for " + window.location.host;
|
||||||
}
|
}
|
||||||
document.getElementById("get-new-address").addEventListener('click', function(e) {
|
document.getElementById("get-new-address").addEventListener('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
37
fly.toml
Normal file
37
fly.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# fly.toml file generated
|
||||||
|
|
||||||
|
kill_signal = "SIGINT"
|
||||||
|
kill_timeout = 5
|
||||||
|
processes = []
|
||||||
|
|
||||||
|
[env]
|
||||||
|
|
||||||
|
[experimental]
|
||||||
|
allowed_public_ports = []
|
||||||
|
auto_rollback = true
|
||||||
|
|
||||||
|
[[services]]
|
||||||
|
http_checks = []
|
||||||
|
internal_port = 1323
|
||||||
|
processes = ["app"]
|
||||||
|
protocol = "tcp"
|
||||||
|
script_checks = []
|
||||||
|
[services.concurrency]
|
||||||
|
hard_limit = 25
|
||||||
|
soft_limit = 20
|
||||||
|
type = "connections"
|
||||||
|
|
||||||
|
[[services.ports]]
|
||||||
|
force_https = true
|
||||||
|
handlers = ["http"]
|
||||||
|
port = 80
|
||||||
|
|
||||||
|
[[services.ports]]
|
||||||
|
handlers = ["tls", "http"]
|
||||||
|
port = 443
|
||||||
|
|
||||||
|
[[services.tcp_checks]]
|
||||||
|
grace_period = "1s"
|
||||||
|
interval = "15s"
|
||||||
|
restart_limit = 0
|
||||||
|
timeout = "2s"
|
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
|||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/didip/tollbooth/v6 v6.1.2
|
github.com/didip/tollbooth/v6 v6.1.2
|
||||||
github.com/knadh/koanf v1.4.1
|
github.com/knadh/koanf v1.4.1
|
||||||
github.com/labstack/echo/v4 v4.7.2
|
github.com/labstack/echo/v4 v4.9.0
|
||||||
github.com/lightningnetwork/lnd v0.14.1-beta
|
github.com/lightningnetwork/lnd v0.14.1-beta
|
||||||
google.golang.org/grpc v1.45.0
|
google.golang.org/grpc v1.45.0
|
||||||
gopkg.in/macaroon.v2 v2.1.0
|
gopkg.in/macaroon.v2 v2.1.0
|
||||||
|
4
go.sum
4
go.sum
@ -463,8 +463,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
|
github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
|
||||||
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||||
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
||||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
@ -47,7 +47,7 @@ type LNDclient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddInvoice generates an invoice with the given price and memo.
|
// AddInvoice generates an invoice with the given price and memo.
|
||||||
func (c LNDclient) AddInvoice(value int64, memo string, descriptionHash []byte) (Invoice, error) {
|
func (c LNDclient) AddInvoice(value int64, memo string, descriptionHash []byte, private bool) (Invoice, error) {
|
||||||
result := Invoice{}
|
result := Invoice{}
|
||||||
|
|
||||||
stdOutLogger.Printf("Adding invoice: memo=%s value=%v", memo, value)
|
stdOutLogger.Printf("Adding invoice: memo=%s value=%v", memo, value)
|
||||||
@ -55,6 +55,7 @@ func (c LNDclient) AddInvoice(value int64, memo string, descriptionHash []byte)
|
|||||||
Memo: memo,
|
Memo: memo,
|
||||||
DescriptionHash: descriptionHash,
|
DescriptionHash: descriptionHash,
|
||||||
Value: value,
|
Value: value,
|
||||||
|
Private: private,
|
||||||
}
|
}
|
||||||
res, err := c.lndClient.AddInvoice(c.ctx, &invoice)
|
res, err := c.lndClient.AddInvoice(c.ctx, &invoice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
14
lnme.go
14
lnme.go
@ -117,7 +117,7 @@ func main() {
|
|||||||
return c.JSON(http.StatusBadRequest, "Bad request")
|
return c.JSON(http.StatusBadRequest, "Bad request")
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice, err := lnClient.AddInvoice(i.Value, i.Memo, nil)
|
invoice, err := lnClient.AddInvoice(i.Value, i.Memo, nil, cfg.Bool("enable-private-channels"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stdOutLogger.Printf("Error creating invoice: %s", err)
|
stdOutLogger.Printf("Error creating invoice: %s", err)
|
||||||
return c.JSON(http.StatusInternalServerError, "Error adding invoice")
|
return c.JSON(http.StatusInternalServerError, "Error adding invoice")
|
||||||
@ -163,6 +163,7 @@ func main() {
|
|||||||
name := c.Param("name")
|
name := c.Param("name")
|
||||||
lightningAddress := name + "@" + host
|
lightningAddress := name + "@" + host
|
||||||
lnurlMetadata := "[[\"text/identifier\", \"" + lightningAddress + "\"], [\"text/plain\", \"Sats for " + lightningAddress + "\"]]"
|
lnurlMetadata := "[[\"text/identifier\", \"" + lightningAddress + "\"], [\"text/plain\", \"Sats for " + lightningAddress + "\"]]"
|
||||||
|
lnurlpCommentAllowed := cfg.Int64("lnurlp-comment-allowed")
|
||||||
|
|
||||||
if amount := c.QueryParam("amount"); amount == "" {
|
if amount := c.QueryParam("amount"); amount == "" {
|
||||||
lnurlPayResponse1 := lnurl.LNURLPayResponse1{
|
lnurlPayResponse1 := lnurl.LNURLPayResponse1{
|
||||||
@ -171,7 +172,7 @@ func main() {
|
|||||||
MinSendable: 1000,
|
MinSendable: 1000,
|
||||||
MaxSendable: 100000000,
|
MaxSendable: 100000000,
|
||||||
EncodedMetadata: lnurlMetadata,
|
EncodedMetadata: lnurlMetadata,
|
||||||
CommentAllowed: 0,
|
CommentAllowed: lnurlpCommentAllowed,
|
||||||
Tag: "payRequest",
|
Tag: "payRequest",
|
||||||
}
|
}
|
||||||
return c.JSON(http.StatusOK, lnurlPayResponse1)
|
return c.JSON(http.StatusOK, lnurlPayResponse1)
|
||||||
@ -183,8 +184,13 @@ func main() {
|
|||||||
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Invalid Amount"})
|
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Invalid Amount"})
|
||||||
}
|
}
|
||||||
sats := msats / 1000 // we need sats
|
sats := msats / 1000 // we need sats
|
||||||
|
comment := c.QueryParam("comment")
|
||||||
|
if commentLength := int64(len(comment)); commentLength > lnurlpCommentAllowed {
|
||||||
|
stdOutLogger.Printf("Invalid comment length: %d", commentLength)
|
||||||
|
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Invalid comment length"})
|
||||||
|
}
|
||||||
metadataHash := sha256.Sum256([]byte(lnurlMetadata))
|
metadataHash := sha256.Sum256([]byte(lnurlMetadata))
|
||||||
invoice, err := lnClient.AddInvoice(sats, lightningAddress, metadataHash[:])
|
invoice, err := lnClient.AddInvoice(sats, comment, metadataHash[:], cfg.Bool("enable-private-channels"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stdOutLogger.Printf("Error creating invoice: %s", err)
|
stdOutLogger.Printf("Error creating invoice: %s", err)
|
||||||
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Server Error"})
|
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Server Error"})
|
||||||
@ -238,9 +244,11 @@ func LoadConfig() *koanf.Koanf {
|
|||||||
f.String("lnd-macaroon", "", "HEX string of LND macaroon file.")
|
f.String("lnd-macaroon", "", "HEX string of LND macaroon file.")
|
||||||
f.String("lnd-cert-path", "~/.lnd/tls.cert", "Path to the LND tls.cert file.")
|
f.String("lnd-cert-path", "~/.lnd/tls.cert", "Path to the LND tls.cert file.")
|
||||||
f.String("lnd-cert", "", "HEX string of LND tls cert file.")
|
f.String("lnd-cert", "", "HEX string of LND tls cert file.")
|
||||||
|
f.Int64("lnurlp-comment-allowed", 210, "Allowed length of LNURL-pay comments.")
|
||||||
f.Bool("disable-website", false, "Disable default embedded website.")
|
f.Bool("disable-website", false, "Disable default embedded website.")
|
||||||
f.Bool("disable-ln-address", false, "Disable Lightning Address handling")
|
f.Bool("disable-ln-address", false, "Disable Lightning Address handling")
|
||||||
f.Bool("disable-cors", false, "Disable CORS headers.")
|
f.Bool("disable-cors", false, "Disable CORS headers.")
|
||||||
|
f.Bool("enable-private-channels", false, "Adds private routing hints to invoices")
|
||||||
f.Float64("request-limit", 5, "Request limit per second.")
|
f.Float64("request-limit", 5, "Request limit per second.")
|
||||||
f.String("static-path", "", "Path to a static assets directory.")
|
f.String("static-path", "", "Path to a static assets directory.")
|
||||||
f.String("port", "", "Port to bind on (deprecated - use listen).")
|
f.String("port", "", "Port to bind on (deprecated - use listen).")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user