WIP CLI
This commit is contained in:
		
							parent
							
								
									1e8421e8ce
								
							
						
					
					
						commit
						a1f513f6a5
					
				
					 9 changed files with 138 additions and 65 deletions
				
			
		|  | @ -34,12 +34,12 @@ type Message struct { | ||||||
| 	Event    string | 	Event    string | ||||||
| 	Time     int64 | 	Time     int64 | ||||||
| 	Topic    string | 	Topic    string | ||||||
|  | 	BaseURL  string | ||||||
|  | 	TopicURL string | ||||||
| 	Message  string | 	Message  string | ||||||
| 	Title    string | 	Title    string | ||||||
| 	Priority int | 	Priority int | ||||||
| 	Tags     []string | 	Tags     []string | ||||||
| 	BaseURL  string |  | ||||||
| 	TopicURL string |  | ||||||
| 	Raw      string | 	Raw      string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -73,7 +73,23 @@ func (c *Client) Publish(topicURL, message string, options ...PublishOption) err | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Client) Subscribe(topicURL string) { | func (c *Client) Poll(topicURL string, options ...SubscribeOption) ([]*Message, error) { | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	messages := make([]*Message, 0) | ||||||
|  | 	msgChan := make(chan *Message) | ||||||
|  | 	errChan := make(chan error) | ||||||
|  | 	go func() { | ||||||
|  | 		err := performSubscribeRequest(ctx, msgChan, topicURL, options...) | ||||||
|  | 		close(msgChan) | ||||||
|  | 		errChan <- err | ||||||
|  | 	}() | ||||||
|  | 	for m := range msgChan { | ||||||
|  | 		messages = append(messages, m) | ||||||
|  | 	} | ||||||
|  | 	return messages, <-errChan | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Client) Subscribe(topicURL string, options ...SubscribeOption) { | ||||||
| 	c.mu.Lock() | 	c.mu.Lock() | ||||||
| 	defer c.mu.Unlock() | 	defer c.mu.Unlock() | ||||||
| 	if _, ok := c.subscriptions[topicURL]; ok { | 	if _, ok := c.subscriptions[topicURL]; ok { | ||||||
|  | @ -81,7 +97,7 @@ func (c *Client) Subscribe(topicURL string) { | ||||||
| 	} | 	} | ||||||
| 	ctx, cancel := context.WithCancel(context.Background()) | 	ctx, cancel := context.WithCancel(context.Background()) | ||||||
| 	c.subscriptions[topicURL] = &subscription{cancel} | 	c.subscriptions[topicURL] = &subscription{cancel} | ||||||
| 	go handleConnectionLoop(ctx, c.Messages, topicURL) | 	go handleSubscribeConnLoop(ctx, c.Messages, topicURL, options...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Client) Unsubscribe(topicURL string) { | func (c *Client) Unsubscribe(topicURL string) { | ||||||
|  | @ -95,25 +111,30 @@ func (c *Client) Unsubscribe(topicURL string) { | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func handleConnectionLoop(ctx context.Context, msgChan chan *Message, topicURL string) { | func handleSubscribeConnLoop(ctx context.Context, msgChan chan *Message, topicURL string, options ...SubscribeOption) { | ||||||
| 	for { | 	for { | ||||||
| 		if err := handleConnection(ctx, msgChan, topicURL); err != nil { | 		if err := performSubscribeRequest(ctx, msgChan, topicURL, options...); err != nil { | ||||||
| 			log.Printf("connection to %s failed: %s", topicURL, err.Error()) | 			log.Printf("Connection to %s failed: %s", topicURL, err.Error()) | ||||||
| 		} | 		} | ||||||
| 		select { | 		select { | ||||||
| 		case <-ctx.Done(): | 		case <-ctx.Done(): | ||||||
| 			log.Printf("connection to %s exited", topicURL) | 			log.Printf("Connection to %s exited", topicURL) | ||||||
| 			return | 			return | ||||||
| 		case <-time.After(5 * time.Second): | 		case <-time.After(5 * time.Second): | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func handleConnection(ctx context.Context, msgChan chan *Message, topicURL string) error { | func performSubscribeRequest(ctx context.Context, msgChan chan *Message, topicURL string, options ...SubscribeOption) error { | ||||||
| 	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/json", topicURL), nil) | 	req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/json", topicURL), nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	for _, option := range options { | ||||||
|  | 		if err := option(req); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	resp, err := http.DefaultClient.Do(req) | 	resp, err := http.DefaultClient.Do(req) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
|  |  | ||||||
|  | @ -4,42 +4,24 @@ import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type PublishOption func(r *http.Request) error | type RequestOption func(r *http.Request) error | ||||||
|  | type PublishOption = RequestOption | ||||||
|  | type SubscribeOption = RequestOption | ||||||
| 
 | 
 | ||||||
| func WithTitle(title string) PublishOption { | func WithTitle(title string) PublishOption { | ||||||
| 	return func(r *http.Request) error { | 	return WithHeader("X-Title", title) | ||||||
| 		if title != "" { |  | ||||||
| 			r.Header.Set("X-Title", title) |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WithPriority(priority string) PublishOption { | func WithPriority(priority string) PublishOption { | ||||||
| 	return func(r *http.Request) error { | 	return WithHeader("X-Priority", priority) | ||||||
| 		if priority != "" { |  | ||||||
| 			r.Header.Set("X-Priority", priority) |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WithTags(tags string) PublishOption { | func WithTags(tags string) PublishOption { | ||||||
| 	return func(r *http.Request) error { | 	return WithHeader("X-Tags", tags) | ||||||
| 		if tags != "" { |  | ||||||
| 			r.Header.Set("X-Tags", tags) |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WithDelay(delay string) PublishOption { | func WithDelay(delay string) PublishOption { | ||||||
| 	return func(r *http.Request) error { | 	return WithHeader("X-Delay", delay) | ||||||
| 		if delay != "" { |  | ||||||
| 			r.Header.Set("X-Delay", delay) |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WithNoCache() PublishOption { | func WithNoCache() PublishOption { | ||||||
|  | @ -50,20 +32,32 @@ func WithNoFirebase() PublishOption { | ||||||
| 	return WithHeader("X-Firebase", "no") | 	return WithHeader("X-Firebase", "no") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WithHeader(header, value string) PublishOption { | func WithSince(since string) SubscribeOption { | ||||||
|  | 	return WithQueryParam("since", since) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func WithPoll() SubscribeOption { | ||||||
|  | 	return WithQueryParam("poll", "1") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func WithScheduled() SubscribeOption { | ||||||
|  | 	return WithQueryParam("scheduled", "1") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func WithHeader(header, value string) RequestOption { | ||||||
| 	return func(r *http.Request) error { | 	return func(r *http.Request) error { | ||||||
| 		r.Header.Set(header, value) | 		if value != "" { | ||||||
|  | 			r.Header.Set(header, value) | ||||||
|  | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type SubscribeOption func(r *http.Request) error | func WithQueryParam(param, value string) RequestOption { | ||||||
| 
 |  | ||||||
| func WithSince(since string) PublishOption { |  | ||||||
| 	return func(r *http.Request) error { | 	return func(r *http.Request) error { | ||||||
| 		if since != "" { | 		if value != "" { | ||||||
| 			q := r.URL.Query() | 			q := r.URL.Query() | ||||||
| 			q.Add("since", since) | 			q.Add(param, value) | ||||||
| 			r.URL.RawQuery = q.Encode() | 			r.URL.RawQuery = q.Encode() | ||||||
| 		} | 		} | ||||||
| 		return nil | 		return nil | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ func New() *cli.App { | ||||||
| 
 | 
 | ||||||
| func execMainApp(c *cli.Context) error { | func execMainApp(c *cli.Context) error { | ||||||
| 	log.Printf("\x1b[1;33mDeprecation notice: Please run the server using 'ntfy serve'; see 'ntfy -h' for help.\x1b[0m") | 	log.Printf("\x1b[1;33mDeprecation notice: Please run the server using 'ntfy serve'; see 'ntfy -h' for help.\x1b[0m") | ||||||
| 	log.Printf("\x1b[1;33mThis way of running the server will be removed Feb 2022.\x1b[0m") | 	log.Printf("\x1b[1;33mThis way of running the server will be removed March 2022. See https://ntfy.sh/docs/deprecations/ for details.\x1b[0m") | ||||||
| 	return execServe(c) | 	return execServe(c) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| var cmdPublish = &cli.Command{ | var cmdPublish = &cli.Command{ | ||||||
| 	Name:      "publish", | 	Name:      "publish", | ||||||
| 	Aliases:   []string{"pub", "send"}, | 	Aliases:   []string{"pub", "send", "push"}, | ||||||
| 	Usage:     "Send message via a ntfy server", | 	Usage:     "Send message via a ntfy server", | ||||||
| 	UsageText: "ntfy send [OPTIONS..] TOPIC MESSAGE", | 	UsageText: "ntfy send [OPTIONS..] TOPIC MESSAGE", | ||||||
| 	Action:    execPublish, | 	Action:    execPublish, | ||||||
|  |  | ||||||
|  | @ -21,6 +21,8 @@ var cmdSubscribe = &cli.Command{ | ||||||
| 	Flags: []cli.Flag{ | 	Flags: []cli.Flag{ | ||||||
| 		&cli.StringFlag{Name: "exec", Aliases: []string{"e"}, Usage: "execute command for each message event"}, | 		&cli.StringFlag{Name: "exec", Aliases: []string{"e"}, Usage: "execute command for each message event"}, | ||||||
| 		&cli.StringFlag{Name: "since", Aliases: []string{"s"}, Usage: "return events since (Unix timestamp, or all)"}, | 		&cli.StringFlag{Name: "since", Aliases: []string{"s"}, Usage: "return events since (Unix timestamp, or all)"}, | ||||||
|  | 		&cli.BoolFlag{Name: "poll", Aliases: []string{"p"}, Usage: "return events and exit, do not listen for new events"}, | ||||||
|  | 		&cli.BoolFlag{Name: "scheduled", Aliases: []string{"sched", "S"}, Usage: "also return scheduled/delayed events"}, | ||||||
| 	}, | 	}, | ||||||
| 	Description: `(THIS COMMAND IS INCUBATING. IT MAY CHANGE WITHOUT NOTICE.) | 	Description: `(THIS COMMAND IS INCUBATING. IT MAY CHANGE WITHOUT NOTICE.) | ||||||
| 
 | 
 | ||||||
|  | @ -45,7 +47,8 @@ are passed to the command as environment variables: | ||||||
| Examples: | Examples: | ||||||
|   ntfy subscribe mytopic                       # Prints JSON for incoming messages to stdout |   ntfy subscribe mytopic                       # Prints JSON for incoming messages to stdout | ||||||
|   ntfy sub home.lan/backups alerts             # Subscribe to two different topics |   ntfy sub home.lan/backups alerts             # Subscribe to two different topics | ||||||
|   ntfy sub --exec='notify-send "$m"' mytopic   # Execute command for incoming messages'   |   ntfy sub --exec='notify-send "$m"' mytopic   # Execute command for incoming messages | ||||||
|  |   ntfy sub --exec=/my/script topic1 topic2     # Subscribe to two topics and execute command for each message | ||||||
| `, | `, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -56,11 +59,37 @@ func execSubscribe(c *cli.Context) error { | ||||||
| 	log.Printf("\x1b[1;33mThis command is incubating. The interface may change without notice.\x1b[0m") | 	log.Printf("\x1b[1;33mThis command is incubating. The interface may change without notice.\x1b[0m") | ||||||
| 	cl := client.DefaultClient | 	cl := client.DefaultClient | ||||||
| 	command := c.String("exec") | 	command := c.String("exec") | ||||||
| 	for _, topic := range c.Args().Slice() { | 	since := c.String("since") | ||||||
| 		cl.Subscribe(expandTopicURL(topic)) | 	poll := c.Bool("poll") | ||||||
|  | 	scheduled := c.Bool("scheduled") | ||||||
|  | 	topics := c.Args().Slice() | ||||||
|  | 	var options []client.SubscribeOption | ||||||
|  | 	if since != "" { | ||||||
|  | 		options = append(options, client.WithSince(since)) | ||||||
| 	} | 	} | ||||||
| 	for m := range cl.Messages { | 	if poll { | ||||||
| 		_ = dispatchMessage(c, command, m) | 		options = append(options, client.WithPoll()) | ||||||
|  | 	} | ||||||
|  | 	if scheduled { | ||||||
|  | 		options = append(options, client.WithScheduled()) | ||||||
|  | 	} | ||||||
|  | 	if poll { | ||||||
|  | 		for _, topic := range topics { | ||||||
|  | 			messages, err := cl.Poll(expandTopicURL(topic), options...) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			for _, m := range messages { | ||||||
|  | 				_ = dispatchMessage(c, command, m) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		for _, topic := range topics { | ||||||
|  | 			cl.Subscribe(expandTopicURL(topic), options...) | ||||||
|  | 		} | ||||||
|  | 		for m := range cl.Messages { | ||||||
|  | 			_ = dispatchMessage(c, command, m) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | @ -77,11 +106,9 @@ func execCommand(c *cli.Context, command string, m *client.Message) error { | ||||||
| 	if m.Event == client.OpenEvent { | 	if m.Event == client.OpenEvent { | ||||||
| 		log.Printf("[%s] Connection opened, subscribed to topic", collapseTopicURL(m.TopicURL)) | 		log.Printf("[%s] Connection opened, subscribed to topic", collapseTopicURL(m.TopicURL)) | ||||||
| 	} else if m.Event == client.MessageEvent { | 	} else if m.Event == client.MessageEvent { | ||||||
| 		go func() { | 		if err := runCommandInternal(c, command, m); err != nil { | ||||||
| 			if err := runCommandInternal(c, command, m); err != nil { | 			log.Printf("[%s] Command failed: %s", collapseTopicURL(m.TopicURL), err.Error()) | ||||||
| 				log.Printf("[%s] Command failed: %s", collapseTopicURL(m.TopicURL), err.Error()) | 		} | ||||||
| 			} |  | ||||||
| 		}() |  | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										25
									
								
								docs/deprecations.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								docs/deprecations.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | # Deprecation notices | ||||||
|  | This page is used to list deprecation notices for ntfy. Deprecated commands and options will be  | ||||||
|  | **removed after ~3 months** from the time they were deprecated. | ||||||
|  | 
 | ||||||
|  | ## Active deprecations | ||||||
|  | 
 | ||||||
|  | ### Running server via `ntfy` (instead of `ntfy serve`) | ||||||
|  | > since 2021-12-17 | ||||||
|  | 
 | ||||||
|  | As more commands are added to the `ntfy` CLI tool, using just `ntfy` to run the server is not practical | ||||||
|  | anymore. Please use `ntfy serve` instead. This also applies to Docker images, as they can also execute more than | ||||||
|  | just the server. | ||||||
|  | 
 | ||||||
|  | === "Before" | ||||||
|  |     ``` | ||||||
|  |     $ ntfy | ||||||
|  |     2021/12/17 08:16:01 Listening on :80/http | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | === "After" | ||||||
|  |     ``` | ||||||
|  |     $ ntfy serve | ||||||
|  |     2021/12/17 08:16:01 Listening on :80/http | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | @ -12,7 +12,7 @@ We support amd64, armv7 and arm64. | ||||||
| 
 | 
 | ||||||
| 1. Install ntfy using one of the methods described below | 1. Install ntfy using one of the methods described below | ||||||
| 2. Then (optionally) edit `/etc/ntfy/config.yml` (see [configuration](config.md)) | 2. Then (optionally) edit `/etc/ntfy/config.yml` (see [configuration](config.md)) | ||||||
| 3. Then just run it with `ntfy` (or `systemctl start ntfy` when using the deb/rpm). | 3. Then just run it with `ntfy serve` (or `systemctl start ntfy` when using the deb/rpm). | ||||||
| 
 | 
 | ||||||
| ## Binaries and packages | ## Binaries and packages | ||||||
| Please check out the [releases page](https://github.com/binwiederhier/ntfy/releases) for binaries and | Please check out the [releases page](https://github.com/binwiederhier/ntfy/releases) for binaries and | ||||||
|  | @ -22,21 +22,21 @@ deb/rpm packages. | ||||||
|     ```bash |     ```bash | ||||||
|     wget https://github.com/binwiederhier/ntfy/releases/download/v1.7.0/ntfy_1.7.0_linux_x86_64.tar.gz |     wget https://github.com/binwiederhier/ntfy/releases/download/v1.7.0/ntfy_1.7.0_linux_x86_64.tar.gz | ||||||
|     sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy |     sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy | ||||||
|     sudo ./ntfy |     sudo ./ntfy serve | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| === "armv7/armhf" | === "armv7/armhf" | ||||||
|     ```bash |     ```bash | ||||||
|     wget https://github.com/binwiederhier/ntfy/releases/download/v1.7.0/ntfy_1.7.0_linux_armv7.tar.gz |     wget https://github.com/binwiederhier/ntfy/releases/download/v1.7.0/ntfy_1.7.0_linux_armv7.tar.gz | ||||||
|     sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy |     sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy | ||||||
|     sudo ./ntfy |     sudo ./ntfy serve | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| === "arm64" | === "arm64" | ||||||
|     ```bash |     ```bash | ||||||
|     wget https://github.com/binwiederhier/ntfy/releases/download/v1.7.0/ntfy_1.7.0_linux_arm64.tar.gz |     wget https://github.com/binwiederhier/ntfy/releases/download/v1.7.0/ntfy_1.7.0_linux_arm64.tar.gz | ||||||
|     sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy |     sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy | ||||||
|     sudo ./ntfy |     sudo ./ntfy serve | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| ## Debian/Ubuntu repository | ## Debian/Ubuntu repository | ||||||
|  | @ -132,12 +132,12 @@ The [ntfy image](https://hub.docker.com/r/binwiederhier/ntfy) is available for a | ||||||
| straight forward to use. | straight forward to use. | ||||||
| 
 | 
 | ||||||
| The server exposes its web UI and the API on port 80, so you need to expose that in Docker. To use the persistent  | The server exposes its web UI and the API on port 80, so you need to expose that in Docker. To use the persistent  | ||||||
| [message cache](config.md#message-cache), you also need to map a volume to `/var/cache/ntfy`. To change other settings, you should map `/etc/ntfy`, | [message cache](config.md#message-cache), you also need to map a volume to `/var/cache/ntfy`. To change other settings,  | ||||||
| so you can edit `/etc/ntfy/config.yml`. | you should map `/etc/ntfy`, so you can edit `/etc/ntfy/config.yml`. | ||||||
| 
 | 
 | ||||||
| Basic usage (no cache or additional config): | Basic usage (no cache or additional config): | ||||||
| ``` | ``` | ||||||
| docker run -p 80:80 -it binwiederhier/ntfy | docker run -p 80:80 -it binwiederhier/ntfy serve | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| With persistent cache (configured as command line arguments): | With persistent cache (configured as command line arguments): | ||||||
|  | @ -147,7 +147,8 @@ docker run \ | ||||||
|   -p 80:80 \ |   -p 80:80 \ | ||||||
|   -it \ |   -it \ | ||||||
|   binwiederhier/ntfy \ |   binwiederhier/ntfy \ | ||||||
|     --cache-file /var/cache/ntfy/cache.db |     --cache-file /var/cache/ntfy/cache.db \ | ||||||
|  |     serve | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| With other config options (configured via `/etc/ntfy/config.yml`, see [configuration](config.md) for details): | With other config options (configured via `/etc/ntfy/config.yml`, see [configuration](config.md) for details): | ||||||
|  | @ -156,7 +157,8 @@ docker run \ | ||||||
|   -v /etc/ntfy:/etc/ntfy \ |   -v /etc/ntfy:/etc/ntfy \ | ||||||
|   -p 80:80 \ |   -p 80:80 \ | ||||||
|   -it \ |   -it \ | ||||||
|   binwiederhier/ntfy |   binwiederhier/ntfy \ | ||||||
|  |   serve | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Go | ## Go | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								docs/subscribe/cli.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/subscribe/cli.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | # Subscribe via CLI | ||||||
|  | 
 | ||||||
|  | XXXXXXXXXxxx | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| site_dir: server/docs | site_dir: server/docs | ||||||
| site_name: ntfy | site_name: ntfy | ||||||
| site_url: https://ntfy.sh | site_url: https://ntfy.sh | ||||||
| site_description: simple HTTP-based pub-sub | site_description: Send push notifications to your phone via PUT/POST | ||||||
| copyright: Made with ❤️ by Philipp C. Heckel | copyright: Made with ❤️ by Philipp C. Heckel | ||||||
| repo_name: binwiederhier/ntfy | repo_name: binwiederhier/ntfy | ||||||
| repo_url: https://github.com/binwiederhier/ntfy | repo_url: https://github.com/binwiederhier/ntfy | ||||||
| edit_uri: edit/main/docs/ | edit_uri: blob/main/docs/ | ||||||
| 
 | 
 | ||||||
| theme: | theme: | ||||||
|   name: material |   name: material | ||||||
|  | @ -31,7 +31,6 @@ theme: | ||||||
|     - search.highlight |     - search.highlight | ||||||
|     - search.share |     - search.share | ||||||
|     - navigation.sections |     - navigation.sections | ||||||
|     # - navigation.instant |  | ||||||
|     - toc.integrate |     - toc.integrate | ||||||
|     - content.tabs.link |     - content.tabs.link | ||||||
| extra: | extra: | ||||||
|  | @ -75,6 +74,7 @@ nav: | ||||||
| - "Subscribing": | - "Subscribing": | ||||||
|   - "From your phone": subscribe/phone.md |   - "From your phone": subscribe/phone.md | ||||||
|   - "From the Web UI": subscribe/web.md |   - "From the Web UI": subscribe/web.md | ||||||
|  |   - "Using the CLI": subscribe/cli.md | ||||||
|   - "Using the API": subscribe/api.md |   - "Using the API": subscribe/api.md | ||||||
| - "Self-hosting": | - "Self-hosting": | ||||||
|   - "Installation": install.md |   - "Installation": install.md | ||||||
|  | @ -83,6 +83,7 @@ nav: | ||||||
|   - "FAQs": faq.md |   - "FAQs": faq.md | ||||||
|   - "Examples": examples.md |   - "Examples": examples.md | ||||||
|   - "Emojis 🥳 🎉": emojis.md |   - "Emojis 🥳 🎉": emojis.md | ||||||
|  |   - "Deprecation notices": deprecations.md | ||||||
|   - "Development": develop.md |   - "Development": develop.md | ||||||
|   - "Privacy policy": privacy.md |   - "Privacy policy": privacy.md | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue