mirror of
https://github.com/mrusme/superhighway84.git
synced 2024-11-15 06:12:48 +00:00
Compare commits
No commits in common. "master" and "v0.0.11" have entirely different histories.
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +0,0 @@
|
|||||||
custom: ["https://github.com/mrusme#support"]
|
|
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@ -10,15 +10,15 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.20
|
go-version: 1.17
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v3
|
uses: goreleaser/goreleaser-action@v2
|
||||||
with:
|
with:
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
version: latest
|
version: latest
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,3 @@
|
|||||||
/orbit*.db
|
/orbit*.db
|
||||||
/newsgroups
|
/newsgroups
|
||||||
|
|
||||||
.gitsigners
|
|
||||||
|
@ -9,8 +9,7 @@ builds:
|
|||||||
- netbsd
|
- netbsd
|
||||||
- openbsd
|
- openbsd
|
||||||
- freebsd
|
- freebsd
|
||||||
# - plan9
|
- plan9
|
||||||
# - android
|
|
||||||
- windows
|
- windows
|
||||||
goarch:
|
goarch:
|
||||||
- 386
|
- 386
|
||||||
@ -29,7 +28,6 @@ builds:
|
|||||||
goarch: arm64
|
goarch: arm64
|
||||||
- goos: freebsd
|
- goos: freebsd
|
||||||
goarm: arm64
|
goarm: arm64
|
||||||
- goos: plan9
|
- goos: plan0
|
||||||
goarm: arm64
|
goarm: arm64
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
Code of Conduct
|
|
||||||
---------------
|
|
||||||
|
|
||||||
If at anytime you choose to do something that a rational person of average
|
|
||||||
intelligence could reasonably consider your actions as "Being a dick", you are
|
|
||||||
in violation of this code of conduct.
|
|
||||||
|
|
298
README.md
298
README.md
@ -1,16 +1,13 @@
|
|||||||
Superhighway84
|
|
||||||
--------------
|
|
||||||
|
|
||||||
[![Superhighway84](superhighway84.jpeg)](superhighway84.png)
|
[![Superhighway84](superhighway84.jpeg)](superhighway84.png)
|
||||||
|
|
||||||
```
|
```
|
||||||
===============================================================================
|
===============================================================================
|
||||||
INTERACTIVE ASYNC / FULL DUPLEX
|
INTERACTIVE ASYNC / FULL DUPLEX
|
||||||
===============================================================================
|
===============================================================================
|
||||||
|
|
||||||
Dial Up To 19.2 Kbps
|
Dial Up To 19.2 Kbps
|
||||||
|
|
||||||
with
|
with
|
||||||
|
|
||||||
_ _ _ __ ____ __ _ __ ___ ____
|
_ _ _ __ ____ __ _ __ ___ ____
|
||||||
/ / / // / __/_ _____ ___ ____/ / (_)__ _/ / _ _____ ___ __( _ )/ / /
|
/ / / // / __/_ _____ ___ ____/ / (_)__ _/ / _ _____ ___ __( _ )/ / /
|
||||||
@ -18,13 +15,13 @@ Superhighway84
|
|||||||
/ / / // /___/\_,_/ .__/\__/_/ /_//_/_/\_, /_//_/__,__/\_,_/\_, /\___/ /_/
|
/ / / // /___/\_,_/ .__/\__/_/ /_//_/_/\_, /_//_/__,__/\_,_/\_, /\___/ /_/
|
||||||
/_/ /___/ /___/
|
/_/ /___/ /___/
|
||||||
|
|
||||||
::: UNCENSORABLE USENET-INSPIRED DECENTRALIZED INTERNET DISCUSSION SYSTEM :::
|
|
||||||
|
::: USENET-INSPIRED DECENTRALIZED INTERNET DISCUSSION SYSTEM :::
|
||||||
|
|
||||||
|
|
||||||
The V.H.S. (Very High Speed) Superhighway84 platform is more than just the
|
The V.H.S. (Very High Speed) Superhighway84 platform is more than just the
|
||||||
fastest decentralized, uncensorable, USENET-inspired communications platform
|
fastest decentralized, USENET-inspired communications platform available. It is
|
||||||
available. It is also the first one to be based on the latest
|
also the first one to be based on the latest IPFS technology available today!
|
||||||
IPFS technology available today!
|
|
||||||
|
|
||||||
Superhighway84 offers the most spectacular features under the Spectrum.
|
Superhighway84 offers the most spectacular features under the Spectrum.
|
||||||
|
|
||||||
@ -34,105 +31,42 @@ Superhighway84
|
|||||||
Long Haul Satellite Operation
|
Long Haul Satellite Operation
|
||||||
Network Diagnostics
|
Network Diagnostics
|
||||||
Fallback Mode
|
Fallback Mode
|
||||||
And More!
|
And More!
|
||||||
|
|
||||||
|
|
||||||
The Superhighway84 modern, uncensorable,
|
The Superhighway84 modern decentralized internet discussion system.
|
||||||
decentralized internet discussion system.
|
It should cost a lot more than $0.
|
||||||
It should cost a lot more than $0.
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
![Screenshot](screenshot01.png)
|
![Screenshot](screenshot01.png)
|
||||||
|
|
||||||
[![Static
|
|
||||||
Badge](https://img.shields.io/badge/Join_on_Matrix-green?style=for-the-badge&logo=element&logoColor=%23ffffff&label=Chat&labelColor=%23333&color=%230DBD8B&link=https%3A%2F%2Fmatrix.to%2F%23%2F%2521PHlbgZTdrhjkCJrfVY%253Amatrix.org)](https://matrix.to/#/%21PHlbgZTdrhjkCJrfVY%3Amatrix.org)
|
|
||||||
|
|
||||||
Superhighway84 is an open source, terminal-based, IPFS-powered, USENET-inspired,
|
|
||||||
uncensorable, decentralized peer-to-peer internet discussion system with retro
|
|
||||||
aesthetics.
|
|
||||||
|
|
||||||
[More info here.](https://xn--gckvb8fzb.com/superhighway84/)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
Download the [kubo 0.16
|
|
||||||
release](https://github.com/ipfs/kubo/releases/tag/v0.16.0) and unpack it:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ tar -xzf ./kubo_*.tar.gz
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If you haven't used IPFS so far, initialize the IPFS repository using the
|
INSTALLATION
|
||||||
following command:
|
------------
|
||||||
|
|
||||||
```sh
|
Clone this repository and run:
|
||||||
$ ./kubo/ipfs init
|
|
||||||
```
|
|
||||||
|
|
||||||
If you had used IPFS an already have an IPFS repository in place, either
|
|
||||||
(re)move it from `~/.ipfs` or make sure to `export IPFS_PATH` before running the
|
|
||||||
`ipfs init` command, e.g.:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ export IPFS_PATH=~/.ipfs-sh84
|
|
||||||
$ ./go-ipfs/ipfs init
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### From Release
|
|
||||||
|
|
||||||
Download the [latest
|
|
||||||
release](https://github.com/mrusme/superhighway84/releases/latest) and unpack
|
|
||||||
it:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ tar -xzf ./superhighway84_*.tar.gz
|
|
||||||
$ ./superhighway84
|
|
||||||
```
|
|
||||||
|
|
||||||
If you initialized the IPFS repo under in a custom location, you need to prefix
|
|
||||||
`IPFS_PATH`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ IPFS_PATH=~/.ipfs-sh84 ./superhighway84
|
|
||||||
```
|
|
||||||
|
|
||||||
The binary `superhighway84` can be moved wherever you please.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### From Source
|
|
||||||
|
|
||||||
Clone this repository
|
|
||||||
|
|
||||||
- from [GitHub](https://github.com/mrusme/superhighway84)
|
|
||||||
```sh
|
|
||||||
$ git clone git@github.com:mrusme/superhighway84.git
|
|
||||||
```
|
|
||||||
- from
|
|
||||||
[Radicle](https://app.radicle.xyz/nodes/seed.xn--gckvb8fzb.com/rad:z4JkpNjyUemfCeU85iqssNEukoBn1)
|
|
||||||
```sh
|
|
||||||
$ rad clone rad:z4JkpNjyUemfCeU85iqssNEukoBn1
|
|
||||||
```
|
|
||||||
|
|
||||||
Then cd into the cloned directory and run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ go build .
|
$ go build .
|
||||||
```
|
|
||||||
|
|
||||||
The binary will be available at ./superhighway84 and can be moved wherever you
|
The binary will be available at ./superhighway84 and can be moved wherever you
|
||||||
please.
|
please.
|
||||||
|
|
||||||
|
If you don't have IPFS installed already, make sure to do so in order to be able
|
||||||
|
to initialize your IPFS repository:
|
||||||
|
|
||||||
|
https://docs.ipfs.io/install/command-line/
|
||||||
|
|
||||||
|
The IPFS repository can be initialized using the following command:
|
||||||
|
|
||||||
|
$ ipfs init
|
||||||
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
RUNNING
|
||||||
|
-------
|
||||||
|
|
||||||
First, check ulimit -n and verify that it's at a reasonable amount. IPFS
|
First, check ulimit -n and verify that it's at a reasonable amount. IPFS
|
||||||
requires it to be large enough (>= 2048) in order to work properly over time.
|
requires it to be large enough (>= 2048) in order to work properly over time.
|
||||||
@ -142,9 +76,7 @@ those flimsy MacBooks, older hardware, a Raspberry or a low-memory VPS it is
|
|||||||
advisable to set the previously created IPFS repository to the `lowpower`
|
advisable to set the previously created IPFS repository to the `lowpower`
|
||||||
profile.
|
profile.
|
||||||
|
|
||||||
```sh
|
|
||||||
$ ipfs config profile apply lowpower
|
$ ipfs config profile apply lowpower
|
||||||
```
|
|
||||||
|
|
||||||
This should help with CPU usage, file descriptors and the amount of network
|
This should help with CPU usage, file descriptors and the amount of network
|
||||||
connections. While during the startup period you might still see peers peaking
|
connections. While during the startup period you might still see peers peaking
|
||||||
@ -153,20 +85,25 @@ and 300 peers.
|
|||||||
|
|
||||||
Afterwards you can simply launch the binary:
|
Afterwards you can simply launch the binary:
|
||||||
|
|
||||||
```sh
|
$ ./superhighway84
|
||||||
$ superhighway84
|
|
||||||
```
|
|
||||||
|
|
||||||
A setup wizard will help you with initial configuration. Please make sure to
|
A setup wizard will help you with initial configuration. Please make sure to
|
||||||
have at least HOME and EDITOR exported in your environment.
|
have at least HOME and EDITOR exported in your environment.
|
||||||
|
|
||||||
|
In case you would like to use a dedicated ipfs repository for Superhighway84,
|
||||||
|
you will have to export a different IPFS_PATH and make sure it was initialized
|
||||||
|
beforehand:
|
||||||
|
|
||||||
|
$ export IPFS_PATH=~/.ipfs-sh84
|
||||||
|
$ ipfs init
|
||||||
|
$ superhighway84
|
||||||
|
|
||||||
In case you're intending to run the official IPFS daemon and Superhighway84 in
|
In case you're intending to run the official IPFS daemon and Superhighway84 in
|
||||||
parallel, be sure to adjust the ports in their respective IPFS repos (e.g.
|
parallel, be sure to adjust the ports in their respective IPFS repos (e.g.
|
||||||
`~/.ipfs` and `~/.ipfs-sh84`) so that they won't utilize the same port numbers.
|
~/.ipfs and ~/.ipfs-sh84) so that they won't utilize the same port numbers.
|
||||||
The ports `4001`, `5001` and `8080` are relevant and should be adjusted to
|
The ports 4001, 5001 and 8080 are relevant and should be adjusted to something
|
||||||
something other for every new repo/IPFS node that will run in parallel, e.g.:
|
other for every new repo/IPFS node that will run in parallel, e.g.:
|
||||||
|
|
||||||
```json
|
|
||||||
"Addresses": {
|
"Addresses": {
|
||||||
"Swarm": [
|
"Swarm": [
|
||||||
"/ip4/0.0.0.0/tcp/4002",
|
"/ip4/0.0.0.0/tcp/4002",
|
||||||
@ -179,9 +116,8 @@ something other for every new repo/IPFS node that will run in parallel, e.g.:
|
|||||||
"API": "/ip4/127.0.0.1/tcp/5002",
|
"API": "/ip4/127.0.0.1/tcp/5002",
|
||||||
"Gateway": "/ip4/127.0.0.1/tcp/8081"
|
"Gateway": "/ip4/127.0.0.1/tcp/8081"
|
||||||
},
|
},
|
||||||
```
|
|
||||||
|
|
||||||
**NOTE**: When running Superhighway84 for the first time it might seem like it's
|
NOTE: When running Superhighway84 for the first time it might seem like it's
|
||||||
"hanging" at the command prompt. Usually it isn't hanging but rather searching
|
"hanging" at the command prompt. Usually it isn't hanging but rather searching
|
||||||
for peer it can connect to in order to synchronize the database. Depending on
|
for peer it can connect to in order to synchronize the database. Depending on
|
||||||
how many people are online, this process might take _some time_, please be
|
how many people are online, this process might take _some time_, please be
|
||||||
@ -189,65 +125,11 @@ patient.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Connectivity
|
USAGE
|
||||||
|
-----
|
||||||
If you're having trouble connecting to the IPFS network that might be due to
|
|
||||||
your network setup. Please try the IPFS `AutoRelay` feature in such a case:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ ipfs config --json Swarm.RelayClient.Enabled true
|
|
||||||
```
|
|
||||||
|
|
||||||
More information on this can be found here:
|
|
||||||
https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#autorelay
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Superhighway84 will guide you through the basic configuration on its first run.
|
|
||||||
The configuration is stored at the path that you specified in the setup wizard.
|
|
||||||
After it was successfully created, it can be adjusted manually and will take
|
|
||||||
effect on the next launch of Superhighway84.
|
|
||||||
|
|
||||||
Configuration options that might be of interest:
|
|
||||||
|
|
||||||
```
|
|
||||||
ArticlesListView =
|
|
||||||
The view to be used for the articles lit. Possible values:
|
|
||||||
0 - threaded view, latest thread at the top
|
|
||||||
1 - list view, latest article at the top
|
|
||||||
|
|
||||||
[Profile]
|
|
||||||
From =
|
|
||||||
The identifier that is being shown when posting an article, e.g. your name,
|
|
||||||
username or email that you'd like to display
|
|
||||||
|
|
||||||
Organization =
|
|
||||||
An optional organization that you'd like to display affiliation with
|
|
||||||
|
|
||||||
[Shortcuts]
|
|
||||||
The shortcuts for navigating Superhighway84, can be reset to its defaults by
|
|
||||||
simply removing the whole [Shortcuts] block and launching Superhighway84
|
|
||||||
|
|
||||||
The structure is as following:
|
|
||||||
|
|
||||||
`<key code> = "event"`
|
|
||||||
|
|
||||||
The key codes can be looked up under the following link:
|
|
||||||
|
|
||||||
https://pkg.go.dev/github.com/gdamore/tcell/v2#Key
|
|
||||||
|
|
||||||
For simple ASCII characters use their ASCII code, e.g. `114` for the character
|
|
||||||
`r`.
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
The default keyboard shortcuts are:
|
The default keyboard shortcuts are:
|
||||||
|
|
||||||
```
|
|
||||||
C-r: Refresh
|
C-r: Refresh
|
||||||
C-h: Focus groups list
|
C-h: Focus groups list
|
||||||
C-l, C-k: Focus articles list
|
C-l, C-k: Focus articles list
|
||||||
@ -262,98 +144,40 @@ C-l, C-k: Focus articles list
|
|||||||
CR: Select item in list
|
CR: Select item in list
|
||||||
n: Publish new article
|
n: Publish new article
|
||||||
r: Reply to selected article
|
r: Reply to selected article
|
||||||
```
|
|
||||||
|
|
||||||
However, you are free to customize these within your configuration file, under
|
However, you are free to customize these within your configuration file, under
|
||||||
the section `Shortcuts`.
|
the section `Shortcuts`. The structure is as following:
|
||||||
|
|
||||||
|
`<key code> = "event"`
|
||||||
|
|
||||||
|
The key codes can be looked up under the following link:
|
||||||
|
|
||||||
|
https://pkg.go.dev/github.com/gdamore/tcell/v2#Key
|
||||||
|
|
||||||
|
For simple ASCII characters use their ASCII code, e.g. `114` for the character
|
||||||
|
`r`.
|
||||||
|
|
||||||
|
|
||||||
### Submit Article
|
KNOWN LIMITATIONS
|
||||||
|
-----------------
|
||||||
When submitting a new article or a reply to an article, the $EDITOR is launched
|
|
||||||
in which a document with a specific structure will be visible. This structure
|
|
||||||
consists of the HEADER, a SEPARATOR and the BODY and looks like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
Subject: This is the subject of the article
|
|
||||||
Newsgroup: test.sandbox
|
|
||||||
= = = = = =
|
|
||||||
This is the multiline
|
|
||||||
body of the article
|
|
||||||
```
|
|
||||||
|
|
||||||
The HEADER contains all headers that are required for an article to be
|
|
||||||
submitted. These are:
|
|
||||||
|
|
||||||
- `Subject:`\
|
|
||||||
The subject of the article that will be shown in the articles list. The
|
|
||||||
subject must only contain of printable ASCII characters.
|
|
||||||
|
|
||||||
- `Newsgroup:`\
|
|
||||||
The newsgroup under which the article will be submitted, this can
|
|
||||||
either be an existing group or a new group. Please try to follow
|
|
||||||
the convention when creating new groups.
|
|
||||||
The newsgroup must only contain of printable ASCII characters.
|
|
||||||
|
|
||||||
The SEPARATOR contains of 6 equal signs and 5 spaces, alternating each
|
|
||||||
other, followed by a new line.
|
|
||||||
|
|
||||||
The BODY can contain of multiline text.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Known Limitations
|
|
||||||
|
|
||||||
- The OrbitDB that Superhighway84 uses is a public database, meaning everyone
|
- The OrbitDB that Superhighway84 uses is a public database, meaning everyone
|
||||||
can alter its data. Since its using a standard _docstore_, PUT and DELETE
|
can alter its data. Since its using a standard _docstore_, PUT and DELETE
|
||||||
events can alter existing data. This issue will be solved in the future by
|
events can alter existing data. This issue will be solved in the future by
|
||||||
customizing the store to ignore these types of events.
|
customizing the store to ignore these types of events.
|
||||||
|
|
||||||
- Superhighway84 is bound to the version of IPFS that Berty decides to support
|
- Probably plenty more that have yet to been found...
|
||||||
for go-orbit-db. go-orbit-db updates, on the other hand, seem to introduce
|
|
||||||
breaking changes from time to time, which are hard to debug as someone without
|
|
||||||
in-depth knowledge nor documentation. Since Superhighway84 is pretty much a
|
|
||||||
one-man-show it would be quite challenging to fork go-orbit-db in order to
|
|
||||||
keep it up to date with IPFS and make its interface more stable. Unfortunately
|
|
||||||
there doesn't seem to be an alternative to Berty's go-orbit-db as of right
|
|
||||||
now, so Superhighway84 is basically stuck with it.
|
|
||||||
If you happen to know your way around IPFS and maybe even go-orbit-db, and
|
|
||||||
would like to support this project, please get in touch!
|
|
||||||
|
|
||||||
- If you have a newer IPFS version installed than the one used by
|
|
||||||
Superhighway84, please make sure to **not upgrade** the IPFS_REPO that
|
|
||||||
Superhighway84 is using. Otherwise you will get an error when starting
|
|
||||||
Superhighway84 that will tell you that there is an IPFS repository mismatch:
|
|
||||||
|
|
||||||
```
|
|
||||||
> panic: Your programs version (11) is lower than your repos (12).
|
|
||||||
```
|
|
||||||
|
|
||||||
If this should be the case, please follow the instructions provided here:
|
|
||||||
|
|
||||||
https://github.com/mrusme/superhighway84/issues/42#issuecomment-1100582472
|
|
||||||
|
|
||||||
- If you encounter the following issue your IPFS repo version might be older
|
|
||||||
than what Superhighway84 is using:
|
|
||||||
|
|
||||||
```
|
|
||||||
> panic: ipfs repo needs migration
|
|
||||||
```
|
|
||||||
|
|
||||||
In this case you might want to follow the IPFS migration guide here:
|
|
||||||
|
|
||||||
https://github.com/ipfs/fs-repo-migrations/blob/master/run.md
|
|
||||||
|
|
||||||
Alternatively use the same IPFS version as used by Superhighway84 to
|
|
||||||
initialize a dedicated Superhighway84 repository. Please refer to the
|
|
||||||
INSTALLATION part for how to do so.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Credits
|
CREDITS
|
||||||
|
-------
|
||||||
|
|
||||||
- Superhighway84 name, code and graphics by [mrusme](https://github.com/mrusme)
|
- Superhighway84 name, code and graphics by mrusme
|
||||||
- Logo backdrop by
|
https://github.com/mrusme
|
||||||
[Swift](https://twitter.com/Swift_1_2/status/1114865117533888512)
|
|
||||||
|
|
||||||
|
- Logo backdrop by Swift
|
||||||
|
https://twitter.com/Swift_1_2/status/1114865117533888512
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
10
cache/cache.go
vendored
10
cache/cache.go
vendored
@ -1,22 +1,22 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/mrusme/superhighway84/models"
|
"github.com/mrusme/superhighway84/models"
|
||||||
"github.com/tidwall/buntdb"
|
"github.com/tidwall/buntdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
db *buntdb.DB
|
db *buntdb.DB
|
||||||
dbPath string
|
dbPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCache(dbPath string) (*Cache, error) {
|
func NewCache(dbPath string) (*Cache, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
cache := new(Cache)
|
cache := new(Cache)
|
||||||
cache.dbPath = dbPath
|
cache.dbPath = dbPath
|
||||||
cache.db, err = buntdb.Open(cache.dbPath)
|
cache.db, err = buntdb.Open(cache.dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
17
common/player-null.go
Normal file
17
common/player-null.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//go:build !poolsuite
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlayer() (*Player) {
|
||||||
|
player := new(Player)
|
||||||
|
|
||||||
|
return player
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Play() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
47
common/player-psfm.go
Normal file
47
common/player-psfm.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//go:build poolsuite
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import(
|
||||||
|
"github.com/mrusme/go-poolsuite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
Poolsuite *poolsuite.Poolsuite
|
||||||
|
poolsuiteLoaded bool
|
||||||
|
poolsuitePlaying bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlayer() (*Player) {
|
||||||
|
player := new(Player)
|
||||||
|
|
||||||
|
player.Poolsuite = poolsuite.NewPoolsuite()
|
||||||
|
player.poolsuiteLoaded = false
|
||||||
|
player.poolsuitePlaying = false
|
||||||
|
|
||||||
|
return player
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) poolsuitePlay() {
|
||||||
|
p.Poolsuite.Play(
|
||||||
|
p.Poolsuite.GetRandomTrackFromPlaylist(
|
||||||
|
p.Poolsuite.GetRandomPlaylist(),
|
||||||
|
),
|
||||||
|
func() { p.poolsuitePlay() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Play() {
|
||||||
|
if p.poolsuiteLoaded == false {
|
||||||
|
p.poolsuiteLoaded = true
|
||||||
|
p.Poolsuite.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.poolsuitePlaying == false {
|
||||||
|
p.poolsuitePlay()
|
||||||
|
p.poolsuitePlaying = true
|
||||||
|
} else {
|
||||||
|
p.Poolsuite.PauseResume()
|
||||||
|
p.poolsuitePlaying = false
|
||||||
|
}
|
||||||
|
}
|
137
config/config.go
137
config/config.go
@ -1,17 +1,17 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigProfile struct {
|
type ConfigProfile struct {
|
||||||
@ -37,7 +37,7 @@ type Config struct {
|
|||||||
ConnectionString string
|
ConnectionString string
|
||||||
|
|
||||||
CachePath string // Deprecated, should be removed soon
|
CachePath string // Deprecated, should be removed soon
|
||||||
DatabaseCachePath string
|
DatabaseCachePath string
|
||||||
ProgramCachePath string
|
ProgramCachePath string
|
||||||
|
|
||||||
Logfile string
|
Logfile string
|
||||||
@ -45,9 +45,6 @@ type Config struct {
|
|||||||
Profile ConfigProfile
|
Profile ConfigProfile
|
||||||
|
|
||||||
Shortcuts map[string]string
|
Shortcuts map[string]string
|
||||||
ShortcutsReference string
|
|
||||||
|
|
||||||
ArticlesListView int8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig() (*Config, error) {
|
func LoadConfig() (*Config, error) {
|
||||||
@ -90,50 +87,28 @@ func LoadConfig() (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) LoadDefaults() (error) {
|
func (cfg *Config) LoadDefaults() (error) {
|
||||||
|
|
||||||
shortcutDefaults := []struct{
|
|
||||||
key tcell.Key
|
|
||||||
command string
|
|
||||||
keyAltText string
|
|
||||||
} {
|
|
||||||
{tcell.KeyCtrlQ, "quit", "C-q"},
|
|
||||||
{tcell.KeyCtrlR, "refresh", "C-r"},
|
|
||||||
{tcell.KeyCtrlH, "focus-groups", "C-h"},
|
|
||||||
{tcell.KeyCtrlL, "focus-articles", "C-l"},
|
|
||||||
{tcell.KeyCtrlK, "focus-articles", "C-k"},
|
|
||||||
{tcell.KeyCtrlJ, "focus-preview", "C-j"},
|
|
||||||
{tcell.KeyCtrlA, "article-mark-all-read", "C-a"},
|
|
||||||
|
|
||||||
{tcell.Key('n'), "article-new", ""},
|
|
||||||
{tcell.Key('r'), "article-reply", ""},
|
|
||||||
|
|
||||||
{tcell.Key('h'), "additional-key-left", ""},
|
|
||||||
{tcell.Key('j'), "additional-key-down", ""},
|
|
||||||
{tcell.Key('k'), "additional-key-up", ""},
|
|
||||||
{tcell.Key('l'), "additional-key-right", ""},
|
|
||||||
|
|
||||||
{tcell.Key('g'), "additional-key-home", ""},
|
|
||||||
{tcell.Key('G'), "additional-key-end", ""},
|
|
||||||
|
|
||||||
{tcell.Key('?'), "help", ""},
|
|
||||||
}
|
|
||||||
|
|
||||||
var sb strings.Builder
|
|
||||||
for _, shortcut := range shortcutDefaults {
|
|
||||||
keyText := string(shortcut.key)
|
|
||||||
if shortcut.keyAltText != "" {
|
|
||||||
keyText = shortcut.keyAltText
|
|
||||||
}
|
|
||||||
sb.WriteString(fmt.Sprintf("%s - %s\n", keyText, shortcut.command))
|
|
||||||
}
|
|
||||||
cfg.ShortcutsReference = sb.String()
|
|
||||||
|
|
||||||
if len(cfg.Shortcuts) == 0 {
|
if len(cfg.Shortcuts) == 0 {
|
||||||
for _, shortcut := range shortcutDefaults {
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyCtrlQ), 10)] = "quit"
|
||||||
cfg.Shortcuts[strconv.FormatInt(int64(shortcut.key), 10)] = shortcut.command
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyCtrlR), 10)] = "refresh"
|
||||||
}
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyCtrlH), 10)] = "focus-groups"
|
||||||
}
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyCtrlL), 10)] = "focus-articles"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyCtrlK), 10)] = "focus-articles"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyCtrlJ), 10)] = "focus-preview"
|
||||||
|
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('n'), 10)] = "article-new"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('r'), 10)] = "article-reply"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyCtrlA), 10)] = "article-mark-all-read"
|
||||||
|
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('h'), 10)] = "additional-key-left"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('j'), 10)] = "additional-key-down"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('k'), 10)] = "additional-key-up"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('l'), 10)] = "additional-key-right"
|
||||||
|
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('g'), 10)] = "additional-key-home"
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64('G'), 10)] = "additional-key-end"
|
||||||
|
|
||||||
|
cfg.Shortcuts[strconv.FormatInt(int64(tcell.KeyF8), 10)] = "play"
|
||||||
|
}
|
||||||
return cfg.Persist()
|
return cfg.Persist()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,10 +127,10 @@ func (cfg *Config) Persist() (error) {
|
|||||||
|
|
||||||
func (cfg *Config) WasSetup() (bool) {
|
func (cfg *Config) WasSetup() (bool) {
|
||||||
if cfg.DatabaseCachePath == "" ||
|
if cfg.DatabaseCachePath == "" ||
|
||||||
cfg.ProgramCachePath == "" ||
|
cfg.ProgramCachePath == "" ||
|
||||||
cfg.ConnectionString == "" ||
|
cfg.ConnectionString == "" ||
|
||||||
cfg.Logfile == "" ||
|
cfg.Logfile == "" ||
|
||||||
cfg.Profile.From == "" {
|
cfg.Profile.From == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,9 +141,9 @@ func (cfg *Config) Setup() (error) {
|
|||||||
fmt.Printf("\nSUPERHIGHWAY84\n\nInitial Setup\n-------------\n\n")
|
fmt.Printf("\nSUPERHIGHWAY84\n\nInitial Setup\n-------------\n\n")
|
||||||
|
|
||||||
defaultConnectionString := "/orbitdb/bafyreifdpagppa7ve45odxuvudz5snbzcybwyfer777huckl4li4zbc5k4/superhighway84"
|
defaultConnectionString := "/orbitdb/bafyreifdpagppa7ve45odxuvudz5snbzcybwyfer777huckl4li4zbc5k4/superhighway84"
|
||||||
if cfg.ConnectionString != "" {
|
if cfg.ConnectionString != "" {
|
||||||
defaultConnectionString = cfg.ConnectionString
|
defaultConnectionString = cfg.ConnectionString
|
||||||
}
|
}
|
||||||
fmt.Printf("Database connection string [%s]: ", defaultConnectionString)
|
fmt.Printf("Database connection string [%s]: ", defaultConnectionString)
|
||||||
fmt.Scanln(&cfg.ConnectionString)
|
fmt.Scanln(&cfg.ConnectionString)
|
||||||
if strings.TrimSpace(cfg.ConnectionString) == "" {
|
if strings.TrimSpace(cfg.ConnectionString) == "" {
|
||||||
@ -181,10 +156,10 @@ func (cfg *Config) Setup() (error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultDatabaseCachePath := filepath.Join(cacheDir, "superhighway84", "database")
|
defaultDatabaseCachePath := filepath.Join(cacheDir, "superhighway84", "database")
|
||||||
// Migration step from old CachePath to new DatabaseCachePath
|
// Migration step from old CachePath to new DatabaseCachePath
|
||||||
if cfg.CachePath != "" {
|
if cfg.CachePath != "" {
|
||||||
defaultDatabaseCachePath = cfg.CachePath
|
defaultDatabaseCachePath = cfg.CachePath
|
||||||
}
|
}
|
||||||
fmt.Printf("Database cache path [%s]: ", defaultDatabaseCachePath)
|
fmt.Printf("Database cache path [%s]: ", defaultDatabaseCachePath)
|
||||||
fmt.Scanln(&cfg.DatabaseCachePath)
|
fmt.Scanln(&cfg.DatabaseCachePath)
|
||||||
if strings.TrimSpace(cfg.DatabaseCachePath) == "" {
|
if strings.TrimSpace(cfg.DatabaseCachePath) == "" {
|
||||||
@ -193,12 +168,12 @@ func (cfg *Config) Setup() (error) {
|
|||||||
os.MkdirAll(filepath.Dir(cfg.DatabaseCachePath), 0755)
|
os.MkdirAll(filepath.Dir(cfg.DatabaseCachePath), 0755)
|
||||||
|
|
||||||
defaultProgramCachePath := filepath.Join(cacheDir, "superhighway84", "program")
|
defaultProgramCachePath := filepath.Join(cacheDir, "superhighway84", "program")
|
||||||
// Migration step from old CachePath to new DatabaseCachePath
|
// Migration step from old CachePath to new DatabaseCachePath
|
||||||
if cfg.CachePath != "" {
|
if cfg.CachePath != "" {
|
||||||
// If the previous CachePath was used, the folder already contains the
|
// If the previous CachePath was used, the folder already contains the
|
||||||
// OrbitDB, hence we need to find a different place
|
// OrbitDB, hence we need to find a different place
|
||||||
defaultProgramCachePath = filepath.Join(cacheDir, "superhighway84.program")
|
defaultProgramCachePath = filepath.Join(cacheDir, "superhighway84.program")
|
||||||
}
|
}
|
||||||
fmt.Printf("Program cache path [%s]: ", defaultProgramCachePath)
|
fmt.Printf("Program cache path [%s]: ", defaultProgramCachePath)
|
||||||
fmt.Scanln(&cfg.ProgramCachePath)
|
fmt.Scanln(&cfg.ProgramCachePath)
|
||||||
if strings.TrimSpace(cfg.ProgramCachePath) == "" {
|
if strings.TrimSpace(cfg.ProgramCachePath) == "" {
|
||||||
@ -207,9 +182,9 @@ func (cfg *Config) Setup() (error) {
|
|||||||
os.MkdirAll(filepath.Dir(cfg.ProgramCachePath), 0755)
|
os.MkdirAll(filepath.Dir(cfg.ProgramCachePath), 0755)
|
||||||
|
|
||||||
defaultLogfile := filepath.Join(cacheDir, "superhighway84.log")
|
defaultLogfile := filepath.Join(cacheDir, "superhighway84.log")
|
||||||
if cfg.Logfile != "" {
|
if cfg.Logfile != "" {
|
||||||
defaultLogfile = cfg.Logfile
|
defaultLogfile = cfg.Logfile
|
||||||
}
|
}
|
||||||
fmt.Printf("Logfile path [%s]: ", defaultLogfile)
|
fmt.Printf("Logfile path [%s]: ", defaultLogfile)
|
||||||
fmt.Scanln(&cfg.Logfile)
|
fmt.Scanln(&cfg.Logfile)
|
||||||
if strings.TrimSpace(cfg.Logfile) == "" {
|
if strings.TrimSpace(cfg.Logfile) == "" {
|
||||||
@ -220,19 +195,19 @@ func (cfg *Config) Setup() (error) {
|
|||||||
fmt.Printf("\nProfile information\n-------------------\n\n")
|
fmt.Printf("\nProfile information\n-------------------\n\n")
|
||||||
|
|
||||||
defaultProfileFrom := fmt.Sprintf("%s@localhost", os.Getenv("USER"))
|
defaultProfileFrom := fmt.Sprintf("%s@localhost", os.Getenv("USER"))
|
||||||
if cfg.Profile.From != "" {
|
if cfg.Profile.From != "" {
|
||||||
defaultProfileFrom = cfg.Profile.From
|
defaultProfileFrom = cfg.Profile.From
|
||||||
}
|
}
|
||||||
fmt.Printf("From [%s]: ", defaultProfileFrom)
|
fmt.Printf("From [%s]: ", defaultProfileFrom)
|
||||||
fmt.Scanln(&cfg.Profile.From)
|
fmt.Scanln(&cfg.Profile.From)
|
||||||
if strings.TrimSpace(cfg.Profile.From) == "" {
|
if strings.TrimSpace(cfg.Profile.From) == "" {
|
||||||
cfg.Profile.From = defaultProfileFrom
|
cfg.Profile.From = defaultProfileFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultProfileOrganization := ""
|
defaultProfileOrganization := ""
|
||||||
if cfg.Profile.Organization != "" {
|
if cfg.Profile.Organization != "" {
|
||||||
defaultProfileOrganization = cfg.Profile.Organization
|
defaultProfileOrganization = cfg.Profile.Organization
|
||||||
}
|
}
|
||||||
fmt.Printf("Organization [%s]: ", defaultProfileOrganization)
|
fmt.Printf("Organization [%s]: ", defaultProfileOrganization)
|
||||||
fmt.Scanln(&cfg.Profile.Organization)
|
fmt.Scanln(&cfg.Profile.Organization)
|
||||||
|
|
||||||
|
@ -4,19 +4,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
orbitdb "berty.tech/go-orbit-db"
|
orbitdb "berty.tech/go-orbit-db"
|
||||||
"berty.tech/go-orbit-db/accesscontroller"
|
"berty.tech/go-orbit-db/accesscontroller"
|
||||||
|
"berty.tech/go-orbit-db/events"
|
||||||
"berty.tech/go-orbit-db/iface"
|
"berty.tech/go-orbit-db/iface"
|
||||||
"berty.tech/go-orbit-db/stores"
|
"berty.tech/go-orbit-db/stores"
|
||||||
"berty.tech/go-orbit-db/stores/documentstore"
|
"berty.tech/go-orbit-db/stores/documentstore"
|
||||||
|
config "github.com/ipfs/go-ipfs-config"
|
||||||
|
"github.com/ipfs/go-ipfs/core"
|
||||||
icore "github.com/ipfs/interface-go-ipfs-core"
|
icore "github.com/ipfs/interface-go-ipfs-core"
|
||||||
config "github.com/ipfs/kubo/config"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
"github.com/ipfs/kubo/core"
|
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
|
||||||
"github.com/libp2p/go-libp2p/core/event"
|
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
@ -25,87 +23,72 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ConnectionString string
|
ConnectionString string
|
||||||
URI string
|
URI string
|
||||||
CachePath string
|
CachePath string
|
||||||
Cache *cache.Cache
|
Cache *cache.Cache
|
||||||
|
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
|
|
||||||
IPFSNode *core.IpfsNode
|
IPFSNode *core.IpfsNode
|
||||||
IPFSCoreAPI icore.CoreAPI
|
IPFSCoreAPI icore.CoreAPI
|
||||||
|
|
||||||
OrbitDB orbitdb.OrbitDB
|
OrbitDB orbitdb.OrbitDB
|
||||||
Store orbitdb.DocumentStore
|
Store orbitdb.DocumentStore
|
||||||
Events event.Subscription
|
StoreEventChan <-chan events.Event
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) init() error {
|
func (db *Database) init() (error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ctx := context.Background()
|
db.OrbitDB, err = orbitdb.NewOrbitDB(db.ctx, db.IPFSCoreAPI, &orbitdb.NewOrbitDBOptions{
|
||||||
|
Directory: &db.CachePath,
|
||||||
|
Logger: db.Logger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
db.Logger.Debug("initializing NewOrbitDB ...")
|
ac := &accesscontroller.CreateAccessControllerOptions{
|
||||||
db.OrbitDB, err = orbitdb.NewOrbitDB(ctx, db.IPFSCoreAPI, &orbitdb.NewOrbitDBOptions{
|
Access: map[string][]string{
|
||||||
Directory: &db.CachePath,
|
"write": {
|
||||||
Logger: db.Logger,
|
"*",
|
||||||
})
|
},
|
||||||
if err != nil {
|
},
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ac := &accesscontroller.CreateAccessControllerOptions{
|
if err != nil {
|
||||||
Access: map[string][]string{
|
return err
|
||||||
"write": {
|
}
|
||||||
"*",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// addr, err := db.OrbitDB.DetermineAddress(db.ctx, db.Name, "docstore", &orbitdb.DetermineAddressOptions{})
|
// addr, err := db.OrbitDB.DetermineAddress(db.ctx, db.Name, "docstore", &orbitdb.DetermineAddressOptions{})
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
// db.URI = addr.String()
|
// db.URI = addr.String()
|
||||||
|
|
||||||
storetype := "docstore"
|
storetype := "docstore"
|
||||||
db.Logger.Debug("initializing OrbitDB.Docs ...")
|
db.Store, err = db.OrbitDB.Docs(db.ctx, db.ConnectionString, &orbitdb.CreateDBOptions{
|
||||||
db.Store, err = db.OrbitDB.Docs(ctx, db.ConnectionString, &orbitdb.CreateDBOptions{
|
AccessController: ac,
|
||||||
AccessController: ac,
|
StoreType: &storetype,
|
||||||
StoreType: &storetype,
|
StoreSpecificOpts: documentstore.DefaultStoreOptsForMap("id"),
|
||||||
StoreSpecificOpts: documentstore.DefaultStoreOptsForMap("id"),
|
})
|
||||||
Timeout: time.Second * 600,
|
if err != nil {
|
||||||
})
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Logger.Debug("subscribing to EventBus ...")
|
db.StoreEventChan = db.Store.Subscribe(db.ctx)
|
||||||
db.Events, err = db.Store.EventBus().Subscribe(new(stores.EventReady))
|
return nil
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) GetOwnID() string {
|
func(db *Database) connectToPeers() error {
|
||||||
return db.OrbitDB.Identity().ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetOwnPubKey() crypto.PubKey {
|
|
||||||
pubKey, err := db.OrbitDB.Identity().GetPublicKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return pubKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) connectToPeers() error {
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
peerInfos, err := config.DefaultBootstrapPeers()
|
peerInfos, err := config.DefaultBootstrapPeers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(len(peerInfos))
|
wg.Add(len(peerInfos))
|
||||||
for _, peerInfo := range peerInfos {
|
for _, peerInfo := range peerInfos {
|
||||||
@ -113,10 +96,10 @@ func (db *Database) connectToPeers() error {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
err := db.IPFSCoreAPI.Swarm().Connect(db.ctx, *peerInfo)
|
err := db.IPFSCoreAPI.Swarm().Connect(db.ctx, *peerInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
db.Logger.Error("failed to connect", zap.String("peerID", peerInfo.ID.String()), zap.Error(err))
|
db.Logger.Debug("failed to connect", zap.String("peerID", peerInfo.ID.String()), zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
db.Logger.Debug("connected!", zap.String("peerID", peerInfo.ID.String()))
|
db.Logger.Debug("connected!", zap.String("peerID", peerInfo.ID.String()))
|
||||||
}
|
}
|
||||||
}(&peerInfo)
|
}(&peerInfo)
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@ -124,186 +107,165 @@ func (db *Database) connectToPeers() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewDatabase(
|
func NewDatabase(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
dbConnectionString string,
|
dbConnectionString string,
|
||||||
dbCache string,
|
dbCache string,
|
||||||
cch *cache.Cache,
|
cch *cache.Cache,
|
||||||
logger *zap.Logger,
|
logger *zap.Logger,
|
||||||
) (*Database, error) {
|
) (*Database, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
db := new(Database)
|
db := new(Database)
|
||||||
db.ctx = ctx
|
db.ctx = ctx
|
||||||
db.ConnectionString = dbConnectionString
|
db.ConnectionString = dbConnectionString
|
||||||
db.CachePath = dbCache
|
db.CachePath = dbCache
|
||||||
db.Cache = cch
|
db.Cache = cch
|
||||||
db.Logger = logger
|
db.Logger = logger
|
||||||
|
|
||||||
db.Logger.Debug("getting config root path ...")
|
defaultPath, err := config.PathRoot()
|
||||||
defaultPath, err := config.PathRoot()
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setupPlugins(defaultPath); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Logger.Debug("setting up plugins ...")
|
db.IPFSNode, db.IPFSCoreAPI, err = createNode(ctx, defaultPath)
|
||||||
if err := setupPlugins(defaultPath); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Logger.Debug("creating IPFS node ...")
|
return db, nil
|
||||||
db.IPFSNode, db.IPFSCoreAPI, err = createNode(ctx, defaultPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return db, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) Connect(onReady func(address string)) error {
|
func (db *Database) Connect(onReady func(address string)) (error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// if db.Init {
|
||||||
|
err = db.init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// } else {
|
||||||
|
// err = db.open()
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
db.Logger.Info("connecting to peers ...")
|
|
||||||
// go func() {
|
// go func() {
|
||||||
err = db.connectToPeers()
|
err = db.connectToPeers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
db.Logger.Error("failed to connect: %s", zap.Error(err))
|
db.Logger.Debug("failed to connect: %s", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
db.Logger.Debug("connected to peer!")
|
db.Logger.Debug("connected to peer!")
|
||||||
}
|
}
|
||||||
// }()
|
// }()
|
||||||
|
|
||||||
db.Logger.Info("initializing database connection ...")
|
// log.Println(db.Store.ReplicationStatus().GetBuffered())
|
||||||
// if db.Init {
|
// log.Println(db.Store.ReplicationStatus().GetQueued())
|
||||||
err = db.init()
|
// log.Println(db.Store.ReplicationStatus().GetProgress())
|
||||||
if err != nil {
|
|
||||||
db.Logger.Error("%s", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// } else {
|
|
||||||
// err = db.open()
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
db.Logger.Info("running ...")
|
db.Logger.Info("running ...")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
for ev := range db.Events.Out() {
|
for ev := range db.StoreEventChan {
|
||||||
db.Logger.Debug("got event", zap.Any("event", ev))
|
db.Logger.Debug("got event", zap.Any("event", ev))
|
||||||
switch ev.(type) {
|
switch ev.(type) {
|
||||||
case stores.EventReady:
|
case *stores.EventReady:
|
||||||
db.URI = db.Store.Address().String()
|
db.URI = db.Store.Address().String()
|
||||||
onReady(db.URI)
|
onReady(db.URI)
|
||||||
continue
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}()
|
||||||
}()
|
|
||||||
|
|
||||||
err = db.Store.Load(db.ctx, -1)
|
err = db.Store.Load(db.ctx, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
db.Logger.Error("%s", zap.Error(err))
|
// TODO: clean up
|
||||||
// TODO: clean up
|
return err
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
db.Logger.Debug("connect done")
|
return nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) Disconnect() {
|
func (db *Database) Disconnect() {
|
||||||
db.Events.Close()
|
db.OrbitDB.Close()
|
||||||
db.Store.Close()
|
|
||||||
db.OrbitDB.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) SubmitArticle(article *models.Article) error {
|
func (db *Database) SubmitArticle(article *models.Article) (error) {
|
||||||
entity, err := structToMap(*article)
|
entity, err := structToMap(*article)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
entity["type"] = "article"
|
entity["type"] = "article"
|
||||||
|
|
||||||
_, err = db.Store.Put(db.ctx, entity)
|
_, err = db.Store.Put(db.ctx, entity)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) GetArticleByID(id string) (models.Article, error) {
|
func (db *Database) GetArticleByID(id string) (models.Article, error) {
|
||||||
entity, err := db.Store.Get(db.ctx, id, &iface.DocumentStoreGetOptions{CaseInsensitive: false})
|
entity, err := db.Store.Get(db.ctx, id, &iface.DocumentStoreGetOptions{CaseInsensitive: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Article{}, err
|
return models.Article{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var article models.Article
|
var article models.Article
|
||||||
err = mapstructure.Decode(entity[0], &article)
|
err = mapstructure.Decode(entity[0], &article)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Article{}, err
|
return models.Article{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return article, nil
|
return article, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) ListArticles() ([]*models.Article, []*models.Article, error) {
|
func (db *Database) ListArticles() ([]*models.Article, []*models.Article, error) {
|
||||||
var articles []*models.Article
|
var articles []*models.Article
|
||||||
var articlesMap = make(map[string]*models.Article)
|
var articlesMap map[string]*models.Article
|
||||||
|
|
||||||
_, err := db.Store.Query(db.ctx, func(e interface{}) (bool, error) {
|
articlesMap = make(map[string]*models.Article)
|
||||||
entity := e.(map[string]interface{})
|
|
||||||
if entity["type"] == "article" {
|
|
||||||
var article models.Article
|
|
||||||
err := mapstructure.Decode(entity, &article)
|
|
||||||
if err == nil {
|
|
||||||
// TODO: Not sure why mapstructure won't convert this field and simply
|
|
||||||
// leave it ""
|
|
||||||
if entity["in-reply-to-id"] != nil {
|
|
||||||
article.InReplyToID = entity["in-reply-to-id"].(string)
|
|
||||||
}
|
|
||||||
db.Cache.LoadArticle(&article)
|
|
||||||
articles = append(articles, &article)
|
|
||||||
articlesMap[article.ID] = articles[(len(articles) - 1)]
|
|
||||||
}
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return articles, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.SliceStable(articles, func(i, j int) bool {
|
_, err := db.Store.Query(db.ctx, func(e interface{})(bool, error) {
|
||||||
return articles[i].Date > articles[j].Date
|
entity := e.(map[string]interface{})
|
||||||
})
|
if entity["type"] == "article" {
|
||||||
|
var article models.Article
|
||||||
|
err := mapstructure.Decode(entity, &article)
|
||||||
|
if err == nil {
|
||||||
|
// TODO: Not sure why mapstructure won't convert this field and simply
|
||||||
|
// leave it ""
|
||||||
|
if entity["in-reply-to-id"] != nil {
|
||||||
|
article.InReplyToID = entity["in-reply-to-id"].(string)
|
||||||
|
}
|
||||||
|
db.Cache.LoadArticle(&article)
|
||||||
|
articles = append(articles, &article)
|
||||||
|
articlesMap[article.ID] = articles[(len(articles) - 1)]
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return articles, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var articlesRoots []*models.Article
|
sort.SliceStable(articles, func(i, j int) bool {
|
||||||
for i := 0; i < len(articles); i++ {
|
return articles[i].Date > articles[j].Date
|
||||||
if articles[i].InReplyToID != "" {
|
})
|
||||||
inReplyTo := articles[i].InReplyToID
|
|
||||||
if _, exist := articlesMap[inReplyTo]; exist {
|
|
||||||
|
|
||||||
(*articlesMap[inReplyTo]).Replies =
|
var articlesRoots []*models.Article
|
||||||
append((*articlesMap[inReplyTo]).Replies, articles[i])
|
for i := 0; i < len(articles); i++ {
|
||||||
(*articlesMap[inReplyTo]).LatestReply = articles[i].Date
|
if articles[i].InReplyToID != "" {
|
||||||
continue
|
if _, exist := articlesMap[articles[i].InReplyToID]; exist == true {
|
||||||
}
|
(*articlesMap[articles[i].InReplyToID]).Replies =
|
||||||
}
|
append((*articlesMap[articles[i].InReplyToID]).Replies, articles[i])
|
||||||
articlesRoots = append(articlesRoots, articles[i])
|
}
|
||||||
}
|
} else {
|
||||||
|
articlesRoots = append(articlesRoots, articles[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sort.SliceStable(articlesRoots, func(i, j int) bool {
|
return articles, articlesRoots, nil
|
||||||
iLatest := articlesRoots[i].LatestReply
|
|
||||||
if iLatest <= 0 {
|
|
||||||
iLatest = articlesRoots[i].Date
|
|
||||||
}
|
|
||||||
|
|
||||||
jLatest := articlesRoots[j].LatestReply
|
|
||||||
if jLatest <= 0 {
|
|
||||||
jLatest = articlesRoots[j].Date
|
|
||||||
}
|
|
||||||
|
|
||||||
return iLatest > jLatest
|
|
||||||
})
|
|
||||||
|
|
||||||
return articles, articlesRoots, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,12 +7,12 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
files "github.com/ipfs/go-ipfs-files"
|
files "github.com/ipfs/go-ipfs-files"
|
||||||
|
"github.com/ipfs/go-ipfs/core"
|
||||||
|
"github.com/ipfs/go-ipfs/core/coreapi"
|
||||||
|
"github.com/ipfs/go-ipfs/core/node/libp2p"
|
||||||
|
"github.com/ipfs/go-ipfs/plugin/loader"
|
||||||
|
"github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||||
icore "github.com/ipfs/interface-go-ipfs-core"
|
icore "github.com/ipfs/interface-go-ipfs-core"
|
||||||
"github.com/ipfs/kubo/core"
|
|
||||||
"github.com/ipfs/kubo/core/coreapi"
|
|
||||||
"github.com/ipfs/kubo/core/node/libp2p"
|
|
||||||
"github.com/ipfs/kubo/plugin/loader"
|
|
||||||
"github.com/ipfs/kubo/repo/fsrepo"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,10 +42,10 @@ func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, icore.Cor
|
|||||||
nodeOptions := &core.BuildCfg{
|
nodeOptions := &core.BuildCfg{
|
||||||
Online: true,
|
Online: true,
|
||||||
Routing: libp2p.DHTClientOption, // DHTOption
|
Routing: libp2p.DHTClientOption, // DHTOption
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
ExtraOpts: map[string]bool{
|
ExtraOpts: map[string]bool{
|
||||||
"pubsub": true,
|
"pubsub": true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := core.NewNode(ctx, nodeOptions)
|
node, err := core.NewNode(ctx, nodeOptions)
|
||||||
@ -53,12 +53,12 @@ func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, icore.Cor
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
coreAPI, err := coreapi.NewCoreAPI(node)
|
coreAPI, err := coreapi.NewCoreAPI(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return node, coreAPI, nil
|
return node, coreAPI, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUnixfsNode(path string) (files.Node, error) {
|
func getUnixfsNode(path string) (files.Node, error) {
|
||||||
@ -76,12 +76,13 @@ func getUnixfsNode(path string) (files.Node, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func structToMap(v interface{}) (map[string]interface{}, error) {
|
func structToMap(v interface{}) (map[string]interface{}, error) {
|
||||||
vMap := &map[string]interface{}{}
|
vMap := &map[string]interface{}{}
|
||||||
|
|
||||||
err := mapstructure.Decode(v, &vMap)
|
err := mapstructure.Decode(v, &vMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return *vMap, nil
|
return *vMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
349
go.mod
349
go.mod
@ -1,235 +1,240 @@
|
|||||||
module github.com/mrusme/superhighway84
|
module github.com/mrusme/superhighway84
|
||||||
|
|
||||||
go 1.20
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
berty.tech/go-orbit-db v1.22.0
|
berty.tech/go-orbit-db v1.13.2
|
||||||
github.com/BurntSushi/toml v1.2.1
|
github.com/ipfs/go-datastore v0.4.5
|
||||||
github.com/eliukblau/pixterm v1.3.1
|
github.com/ipfs/go-filestore v0.0.3
|
||||||
github.com/gdamore/tcell/v2 v2.6.0
|
github.com/ipfs/go-ipfs v0.9.1
|
||||||
github.com/go-playground/validator/v10 v10.12.0
|
github.com/ipfs/go-ipfs-config v0.14.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/ipfs/go-ipfs-keystore v0.0.2
|
||||||
github.com/ipfs/go-ipfs-files v0.3.0
|
github.com/libp2p/go-libp2p v0.14.3
|
||||||
github.com/ipfs/interface-go-ipfs-core v0.11.1
|
github.com/libp2p/go-libp2p-core v0.8.5
|
||||||
github.com/ipfs/kubo v0.19.0
|
|
||||||
github.com/libp2p/go-libp2p v0.27.8
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
|
||||||
github.com/rivo/tview v0.0.0-20230330183452-5796b0cd5c1f
|
|
||||||
github.com/tidwall/buntdb v1.2.10
|
|
||||||
go.uber.org/zap v1.24.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect
|
bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect
|
||||||
berty.tech/go-ipfs-log v1.10.0 // indirect
|
berty.tech/go-ipfs-log v1.5.0 // indirect
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
github.com/BurntSushi/toml v0.4.1 // indirect
|
||||||
|
github.com/Stebalien/go-bitfield v0.0.1 // indirect
|
||||||
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect
|
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect
|
||||||
github.com/benbjohnson/clock v1.3.0 // indirect
|
github.com/benbjohnson/clock v1.0.3 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/btcsuite/btcd v0.22.1 // indirect
|
github.com/btcsuite/btcd v0.21.0-beta // indirect
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
|
||||||
github.com/ceramicnetwork/go-dag-jose v0.1.0 // indirect
|
|
||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||||
github.com/containerd/cgroups v1.1.0 // indirect
|
github.com/cheekybits/genny v1.0.0 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
|
||||||
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
|
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
|
||||||
github.com/cskr/pubsub v1.0.2 // indirect
|
github.com/cskr/pubsub v1.0.2 // indirect
|
||||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
|
||||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||||
github.com/dgraph-io/ristretto v0.0.3 // indirect
|
github.com/dgraph-io/ristretto v0.0.2 // indirect
|
||||||
github.com/disintegration/imaging v1.6.2 // indirect
|
github.com/disintegration/imaging v1.6.2 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||||
github.com/elastic/gosigar v0.14.2 // indirect
|
github.com/eliukblau/pixterm v1.3.1 // indirect
|
||||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
|
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
|
||||||
|
github.com/faiface/beep v1.1.0 // indirect
|
||||||
github.com/flynn/noise v1.0.0 // indirect
|
github.com/flynn/noise v1.0.0 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
github.com/gdamore/encoding v1.0.0 // indirect
|
github.com/gdamore/encoding v1.0.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // indirect
|
github.com/gdamore/tcell v1.4.0 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-playground/validator/v10 v10.9.0 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
github.com/google/gopacket v1.1.19 // indirect
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
github.com/hajimehoshi/go-mp3 v0.3.0 // indirect
|
||||||
|
github.com/hajimehoshi/oto v0.7.1 // indirect
|
||||||
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect
|
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect
|
github.com/huin/goupnp v1.0.0 // indirect
|
||||||
github.com/huin/goupnp v1.1.0 // indirect
|
|
||||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||||
github.com/ipfs/go-bitfield v1.1.0 // indirect
|
github.com/ipfs/go-bitswap v0.3.4 // indirect
|
||||||
github.com/ipfs/go-block-format v0.1.1 // indirect
|
github.com/ipfs/go-block-format v0.0.3 // indirect
|
||||||
github.com/ipfs/go-blockservice v0.5.0 // indirect
|
github.com/ipfs/go-blockservice v0.1.4 // indirect
|
||||||
github.com/ipfs/go-cid v0.4.1 // indirect
|
github.com/ipfs/go-cid v0.0.7 // indirect
|
||||||
github.com/ipfs/go-cidutil v0.1.0 // indirect
|
github.com/ipfs/go-cidutil v0.0.2 // indirect
|
||||||
github.com/ipfs/go-datastore v0.6.0 // indirect
|
github.com/ipfs/go-ds-badger v0.2.6 // indirect
|
||||||
github.com/ipfs/go-delegated-routing v0.7.0 // indirect
|
github.com/ipfs/go-ds-flatfs v0.4.5 // indirect
|
||||||
github.com/ipfs/go-ds-badger v0.3.0 // indirect
|
github.com/ipfs/go-ds-leveldb v0.4.2 // indirect
|
||||||
github.com/ipfs/go-ds-flatfs v0.5.1 // indirect
|
github.com/ipfs/go-ds-measure v0.1.0 // indirect
|
||||||
github.com/ipfs/go-ds-leveldb v0.5.0 // indirect
|
github.com/ipfs/go-fs-lock v0.0.6 // indirect
|
||||||
github.com/ipfs/go-ds-measure v0.2.0 // indirect
|
github.com/ipfs/go-graphsync v0.8.0 // indirect
|
||||||
github.com/ipfs/go-fetcher v1.6.1 // indirect
|
github.com/ipfs/go-ipfs-blockstore v0.1.6 // indirect
|
||||||
github.com/ipfs/go-filestore v1.2.0 // indirect
|
|
||||||
github.com/ipfs/go-fs-lock v0.0.7 // indirect
|
|
||||||
github.com/ipfs/go-graphsync v0.14.1 // indirect
|
|
||||||
github.com/ipfs/go-ipfs-blockstore v1.2.0 // indirect
|
|
||||||
github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect
|
github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect
|
||||||
|
github.com/ipfs/go-ipfs-cmds v0.6.0 // indirect
|
||||||
github.com/ipfs/go-ipfs-delay v0.0.1 // indirect
|
github.com/ipfs/go-ipfs-delay v0.0.1 // indirect
|
||||||
github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect
|
github.com/ipfs/go-ipfs-ds-help v0.1.1 // indirect
|
||||||
github.com/ipfs/go-ipfs-exchange-interface v0.2.0 // indirect
|
github.com/ipfs/go-ipfs-exchange-interface v0.0.1 // indirect
|
||||||
github.com/ipfs/go-ipfs-exchange-offline v0.3.0 // indirect
|
github.com/ipfs/go-ipfs-exchange-offline v0.0.1 // indirect
|
||||||
github.com/ipfs/go-ipfs-keystore v0.1.0 // indirect
|
github.com/ipfs/go-ipfs-files v0.0.8 // indirect
|
||||||
github.com/ipfs/go-ipfs-pinner v0.3.0 // indirect
|
github.com/ipfs/go-ipfs-pinner v0.1.1 // indirect
|
||||||
github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect
|
github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect
|
||||||
github.com/ipfs/go-ipfs-pq v0.0.3 // indirect
|
github.com/ipfs/go-ipfs-pq v0.0.2 // indirect
|
||||||
github.com/ipfs/go-ipfs-provider v0.8.1 // indirect
|
github.com/ipfs/go-ipfs-provider v0.5.1 // indirect
|
||||||
github.com/ipfs/go-ipfs-routing v0.3.0 // indirect
|
github.com/ipfs/go-ipfs-routing v0.1.0 // indirect
|
||||||
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
|
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
|
||||||
github.com/ipfs/go-ipld-cbor v0.0.6 // indirect
|
github.com/ipfs/go-ipld-cbor v0.0.5 // indirect
|
||||||
github.com/ipfs/go-ipld-format v0.4.0 // indirect
|
github.com/ipfs/go-ipld-format v0.2.0 // indirect
|
||||||
github.com/ipfs/go-ipld-git v0.1.1 // indirect
|
github.com/ipfs/go-ipld-git v0.0.4 // indirect
|
||||||
github.com/ipfs/go-ipld-legacy v0.1.1 // indirect
|
github.com/ipfs/go-ipns v0.1.0 // indirect
|
||||||
github.com/ipfs/go-ipns v0.3.0 // indirect
|
|
||||||
github.com/ipfs/go-libipfs v0.6.2 // indirect
|
|
||||||
github.com/ipfs/go-log v1.0.5 // indirect
|
github.com/ipfs/go-log v1.0.5 // indirect
|
||||||
github.com/ipfs/go-log/v2 v2.5.1 // indirect
|
github.com/ipfs/go-log/v2 v2.1.3 // indirect
|
||||||
github.com/ipfs/go-merkledag v0.10.0 // indirect
|
github.com/ipfs/go-merkledag v0.3.2 // indirect
|
||||||
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
|
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
|
||||||
github.com/ipfs/go-mfs v0.2.1 // indirect
|
github.com/ipfs/go-mfs v0.1.2 // indirect
|
||||||
github.com/ipfs/go-namesys v0.7.0 // indirect
|
github.com/ipfs/go-namesys v0.3.0 // indirect
|
||||||
github.com/ipfs/go-path v0.3.1 // indirect
|
github.com/ipfs/go-path v0.0.9 // indirect
|
||||||
github.com/ipfs/go-peertaskqueue v0.8.1 // indirect
|
github.com/ipfs/go-peertaskqueue v0.2.0 // indirect
|
||||||
github.com/ipfs/go-unixfs v0.4.4 // indirect
|
github.com/ipfs/go-unixfs v0.2.5 // indirect
|
||||||
github.com/ipfs/go-unixfsnode v1.5.2 // indirect
|
github.com/ipfs/go-verifcid v0.0.1 // indirect
|
||||||
github.com/ipfs/go-verifcid v0.0.2 // indirect
|
github.com/ipfs/interface-go-ipfs-core v0.4.0 // indirect
|
||||||
github.com/ipld/edelweiss v0.2.0 // indirect
|
github.com/ipld/go-codec-dagpb v1.2.0 // indirect
|
||||||
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
|
github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db // indirect
|
||||||
github.com/ipld/go-ipld-prime v0.20.0 // indirect
|
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||||
github.com/jbenet/goprocess v0.1.4 // indirect
|
github.com/jbenet/goprocess v0.1.4 // indirect
|
||||||
github.com/klauspost/compress v1.16.4 // indirect
|
github.com/klauspost/compress v1.11.7 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.4 // indirect
|
||||||
github.com/koron/go-ssdp v0.0.4 // indirect
|
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d // indirect
|
||||||
github.com/leodido/go-urn v1.2.2 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
github.com/libp2p/go-addr-util v0.0.2 // indirect
|
||||||
|
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
|
||||||
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
||||||
github.com/libp2p/go-doh-resolver v0.4.0 // indirect
|
github.com/libp2p/go-conn-security-multistream v0.2.1 // indirect
|
||||||
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
|
github.com/libp2p/go-doh-resolver v0.3.1 // indirect
|
||||||
github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect
|
github.com/libp2p/go-eventbus v0.2.1 // indirect
|
||||||
github.com/libp2p/go-libp2p-kad-dht v0.21.1 // indirect
|
github.com/libp2p/go-flow-metrics v0.0.3 // indirect
|
||||||
github.com/libp2p/go-libp2p-kbucket v0.5.0 // indirect
|
github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052 // indirect
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.9.3 // indirect
|
github.com/libp2p/go-libp2p-autonat v0.4.2 // indirect
|
||||||
github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect
|
github.com/libp2p/go-libp2p-blankhost v0.2.0 // indirect
|
||||||
github.com/libp2p/go-libp2p-record v0.2.0 // indirect
|
github.com/libp2p/go-libp2p-circuit v0.4.0 // indirect
|
||||||
github.com/libp2p/go-libp2p-routing-helpers v0.6.1 // indirect
|
github.com/libp2p/go-libp2p-connmgr v0.2.4 // indirect
|
||||||
github.com/libp2p/go-libp2p-xor v0.1.0 // indirect
|
github.com/libp2p/go-libp2p-discovery v0.5.1 // indirect
|
||||||
github.com/libp2p/go-mplex v0.7.0 // indirect
|
github.com/libp2p/go-libp2p-kad-dht v0.12.2 // indirect
|
||||||
github.com/libp2p/go-msgio v0.3.0 // indirect
|
github.com/libp2p/go-libp2p-kbucket v0.4.7 // indirect
|
||||||
github.com/libp2p/go-nat v0.1.0 // indirect
|
github.com/libp2p/go-libp2p-loggables v0.1.0 // indirect
|
||||||
github.com/libp2p/go-netroute v0.2.1 // indirect
|
github.com/libp2p/go-libp2p-mplex v0.4.1 // indirect
|
||||||
github.com/libp2p/go-reuseport v0.2.0 // indirect
|
github.com/libp2p/go-libp2p-nat v0.0.6 // indirect
|
||||||
github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
|
github.com/libp2p/go-libp2p-netutil v0.1.0 // indirect
|
||||||
github.com/libp2p/zeroconf/v2 v2.2.0 // indirect
|
github.com/libp2p/go-libp2p-noise v0.2.0 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-peerstore v0.2.7 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-pnet v0.2.0 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-pubsub v0.4.2 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-pubsub-router v0.4.0 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-quic-transport v0.11.2 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-record v0.1.3 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-swarm v0.5.0 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-testing v0.4.0 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-tls v0.1.3 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-transport-upgrader v0.4.2 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-xor v0.0.0-20200501025846-71e284145d58 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-yamux v0.5.4 // indirect
|
||||||
|
github.com/libp2p/go-maddr-filter v0.1.0 // indirect
|
||||||
|
github.com/libp2p/go-mplex v0.3.0 // indirect
|
||||||
|
github.com/libp2p/go-msgio v0.0.6 // indirect
|
||||||
|
github.com/libp2p/go-nat v0.0.5 // indirect
|
||||||
|
github.com/libp2p/go-netroute v0.1.6 // indirect
|
||||||
|
github.com/libp2p/go-openssl v0.0.7 // indirect
|
||||||
|
github.com/libp2p/go-reuseport v0.0.2 // indirect
|
||||||
|
github.com/libp2p/go-reuseport-transport v0.0.4 // indirect
|
||||||
|
github.com/libp2p/go-sockaddr v0.1.1 // indirect
|
||||||
|
github.com/libp2p/go-stream-muxer-multistream v0.3.0 // indirect
|
||||||
|
github.com/libp2p/go-tcp-transport v0.2.4 // indirect
|
||||||
|
github.com/libp2p/go-ws-transport v0.4.0 // indirect
|
||||||
|
github.com/libp2p/go-yamux/v2 v2.2.0 // indirect
|
||||||
|
github.com/lucas-clemente/quic-go v0.21.2 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-15 v0.1.5 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
|
||||||
|
github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1 // indirect
|
||||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/miekg/dns v1.1.41 // indirect
|
||||||
github.com/miekg/dns v1.1.53 // indirect
|
|
||||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||||
|
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
github.com/mrusme/go-poolsuite v0.0.0-20220102191132-9dd8514d3e05 // indirect
|
||||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
github.com/multiformats/go-base32 v0.0.3 // indirect
|
||||||
github.com/multiformats/go-multiaddr v0.9.0 // indirect
|
github.com/multiformats/go-base36 v0.1.0 // indirect
|
||||||
|
github.com/multiformats/go-multiaddr v0.3.3 // indirect
|
||||||
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
|
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
|
||||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||||
github.com/multiformats/go-multibase v0.2.0 // indirect
|
github.com/multiformats/go-multiaddr-net v0.2.0 // indirect
|
||||||
github.com/multiformats/go-multicodec v0.8.1 // indirect
|
github.com/multiformats/go-multibase v0.0.3 // indirect
|
||||||
github.com/multiformats/go-multihash v0.2.1 // indirect
|
github.com/multiformats/go-multicodec v0.2.0 // indirect
|
||||||
github.com/multiformats/go-multistream v0.4.1 // indirect
|
github.com/multiformats/go-multihash v0.0.15 // indirect
|
||||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
github.com/multiformats/go-multistream v0.2.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
github.com/multiformats/go-varint v0.0.6 // indirect
|
||||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
github.com/openzipkin/zipkin-go v0.4.0 // indirect
|
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/polydawn/refmt v0.89.0 // indirect
|
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect
|
||||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
github.com/prometheus/client_golang v1.10.0 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/prometheus/common v0.42.0 // indirect
|
github.com/prometheus/common v0.18.0 // indirect
|
||||||
github.com/prometheus/procfs v0.9.0 // indirect
|
github.com/prometheus/procfs v0.6.0 // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/rivo/tview v0.0.0-20211202162923-2a6de950f73b // indirect
|
||||||
github.com/quic-go/quic-go v0.42.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/quic-go/webtransport-go v0.5.2 // indirect
|
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
|
||||||
github.com/raulk/go-watchdog v1.3.0 // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.3 // indirect
|
|
||||||
github.com/samber/lo v1.36.0 // indirect
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
github.com/tidwall/btree v1.4.2 // indirect
|
github.com/tidwall/btree v1.1.0 // indirect
|
||||||
github.com/tidwall/gjson v1.14.3 // indirect
|
github.com/tidwall/buntdb v1.2.9 // indirect
|
||||||
|
github.com/tidwall/gjson v1.12.1 // indirect
|
||||||
github.com/tidwall/grect v0.1.4 // indirect
|
github.com/tidwall/grect v0.1.4 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/tidwall/rtred v0.1.2 // indirect
|
github.com/tidwall/rtred v0.1.2 // indirect
|
||||||
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
||||||
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
|
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
|
||||||
github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa // indirect
|
github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 // indirect
|
||||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
||||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
||||||
|
github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 // indirect
|
||||||
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect
|
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
|
||||||
go.opentelemetry.io/otel v1.11.1 // indirect
|
go.opencensus.io v0.23.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.7.0 // indirect
|
go.opentelemetry.io/otel v0.20.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect
|
go.opentelemetry.io/otel/trace v0.20.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 // indirect
|
go.uber.org/dig v1.10.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 // indirect
|
go.uber.org/fx v1.13.1 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.7.0 // indirect
|
go.uber.org/zap v1.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
|
|
||||||
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
|
||||||
go.opentelemetry.io/proto/otlp v0.16.0 // indirect
|
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
|
||||||
go.uber.org/dig v1.16.1 // indirect
|
|
||||||
go.uber.org/fx v1.19.2 // indirect
|
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
|
||||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd // indirect
|
||||||
golang.org/x/image v0.18.0 // indirect
|
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||||
golang.org/x/term v0.20.0 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/tools v0.1.1 // indirect
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
google.golang.org/grpc v1.33.2 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
google.golang.org/protobuf v1.26.0 // indirect
|
||||||
google.golang.org/grpc v1.56.3 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
|
||||||
lukechampine.com/blake3 v1.1.7 // indirect
|
|
||||||
nhooyr.io/websocket v1.8.7 // indirect
|
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Article struct {
|
type Article struct {
|
||||||
@ -18,9 +18,7 @@ type Article struct {
|
|||||||
Body string `mapstructure:"body" json:"-" validate:"required,min=3,max=524288"`
|
Body string `mapstructure:"body" json:"-" validate:"required,min=3,max=524288"`
|
||||||
|
|
||||||
Replies []*Article `mapstructure:"-" json:"-" validate:"-"`
|
Replies []*Article `mapstructure:"-" json:"-" validate:"-"`
|
||||||
LatestReply int64 `mapstructure:"-" json:"-" validate:"-"`
|
Read bool `mapstructure:"-" json:"read" validate:"-"`
|
||||||
|
|
||||||
Read bool `mapstructure:"-" json:"read" validate:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArticle() (*Article) {
|
func NewArticle() (*Article) {
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/mrusme/superhighway84/cache"
|
"github.com/mrusme/superhighway84/cache"
|
||||||
"github.com/mrusme/superhighway84/config"
|
"github.com/mrusme/superhighway84/config"
|
||||||
"github.com/mrusme/superhighway84/database"
|
"github.com/mrusme/superhighway84/database"
|
||||||
"github.com/mrusme/superhighway84/models"
|
"github.com/mrusme/superhighway84/models"
|
||||||
"github.com/mrusme/superhighway84/tui"
|
"github.com/mrusme/superhighway84/tui"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed superhighway84.jpeg
|
//go:embed superhighway84.jpeg
|
||||||
@ -32,7 +32,7 @@ func NewLogger(filename string) (*zap.Logger, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := zap.NewDevelopmentConfig()
|
cfg := zap.NewProductionConfig()
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
cfg.OutputPaths = []string{
|
cfg.OutputPaths = []string{
|
||||||
"stdout",
|
"stdout",
|
||||||
@ -43,7 +43,6 @@ func NewLogger(filename string) (*zap.Logger, error) {
|
|||||||
filename,
|
filename,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg.Build()
|
return cfg.Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +50,6 @@ func main() {
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
log.Println("loading configuration ...")
|
|
||||||
cfg, err := config.LoadConfig()
|
cfg, err := config.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
@ -60,13 +58,11 @@ func main() {
|
|||||||
cfg.Setup()
|
cfg.Setup()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("initializing logger ...")
|
|
||||||
logger, err := NewLogger(cfg.Logfile)
|
logger, err := NewLogger(cfg.Logfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("initializing cache ...")
|
|
||||||
cch, err := cache.NewCache(cfg.ProgramCachePath)
|
cch, err := cache.NewCache(cfg.ProgramCachePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
@ -76,14 +72,12 @@ func main() {
|
|||||||
var articles []*models.Article
|
var articles []*models.Article
|
||||||
var articlesRoots []*models.Article
|
var articlesRoots []*models.Article
|
||||||
|
|
||||||
log.Println("initializing TUI and loading database, please wait ...")
|
|
||||||
TUI := tui.Init(&EMBEDFS, cfg, cch, logger)
|
TUI := tui.Init(&EMBEDFS, cfg, cch, logger)
|
||||||
TUI.SetVersion(strings.TrimLeft(version, "v"), strings.TrimLeft(getLatestVersion(), "v") )
|
TUI.SetVersion(strings.TrimLeft(version, "v"), strings.TrimLeft(getLatestVersion(), "v") )
|
||||||
|
|
||||||
TUI.ArticlesDatasource = &articles
|
TUI.ArticlesDatasource = &articles
|
||||||
TUI.ArticlesRoots = &articlesRoots
|
TUI.ArticlesRoots = &articlesRoots
|
||||||
|
|
||||||
log.Println("initializing database ...")
|
|
||||||
db, err := database.NewDatabase(ctx, cfg.ConnectionString, cfg.DatabaseCachePath, cch, logger)
|
db, err := database.NewDatabase(ctx, cfg.ConnectionString, cfg.DatabaseCachePath, cch, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
@ -92,28 +86,16 @@ func main() {
|
|||||||
|
|
||||||
TUI.CallbackRefreshArticles = func() (error) {
|
TUI.CallbackRefreshArticles = func() (error) {
|
||||||
articles, articlesRoots, err = db.ListArticles()
|
articles, articlesRoots, err = db.ListArticles()
|
||||||
if err != nil {
|
|
||||||
logger.Error("%s", zap.Error(err))
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
TUI.CallbackSubmitArticle = func(article *models.Article) (error) {
|
TUI.CallbackSubmitArticle = func(article *models.Article) (error) {
|
||||||
err := db.SubmitArticle(article)
|
return db.SubmitArticle(article)
|
||||||
if err != nil {
|
|
||||||
logger.Error("%s", zap.Error(err))
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("connecting database ...")
|
|
||||||
err = db.Connect(func(address string) {
|
err = db.Connect(func(address string) {
|
||||||
TUI.Meta["myID"] = db.GetOwnID()
|
|
||||||
TUI.Meta["myPubKey"] = db.GetOwnPubKey()
|
|
||||||
|
|
||||||
TUI.Views["mainscreen"].(*tui.Mainscreen).SetFooter(address)
|
TUI.Views["mainscreen"].(*tui.Mainscreen).SetFooter(address)
|
||||||
articles, articlesRoots, _ = db.ListArticles()
|
articles, articlesRoots, _ = db.ListArticles()
|
||||||
|
|
||||||
|
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
TUI.SetView("mainscreen", true)
|
TUI.SetView("mainscreen", true)
|
||||||
|
|
||||||
@ -133,8 +115,6 @@ func main() {
|
|||||||
connections, err := db.IPFSCoreAPI.Swarm().Peers(context.Background())
|
connections, err := db.IPFSCoreAPI.Swarm().Peers(context.Background())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
peers = len(connections)
|
peers = len(connections)
|
||||||
} else {
|
|
||||||
logger.Error("%s", zap.Error(err))
|
|
||||||
}
|
}
|
||||||
TUI.SetStats(int64(peers), int64(bw.RateIn), int64(bw.RateOut), bw.TotalIn , bw.TotalOut)
|
TUI.SetStats(int64(peers), int64(bw.RateIn), int64(bw.RateOut), bw.TotalIn , bw.TotalOut)
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mrusme/superhighway84/models"
|
"github.com/mrusme/superhighway84/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MillisecondsToDate(ms int64) (string) {
|
func MillisecondsToDate(ms int64) (string) {
|
||||||
@ -30,8 +30,8 @@ func (t *TUI) OpenArticle(article *models.Article, readOnly bool) (models.Articl
|
|||||||
defer os.Remove(tmpFile.Name())
|
defer os.Remove(tmpFile.Name())
|
||||||
|
|
||||||
tmpContent := []byte(fmt.Sprintf(
|
tmpContent := []byte(fmt.Sprintf(
|
||||||
"Subject: %s\nNewsgroup: %s\nFrom: %s\n= = = = = =\n%s",
|
"Subject: %s\nNewsgroup: %s\n= = = = = =\n%s",
|
||||||
article.Subject, article.Newsgroup, article.From, article.Body))
|
article.Subject, article.Newsgroup, article.Body))
|
||||||
if _, err = tmpFile.Write(tmpContent); err != nil {
|
if _, err = tmpFile.Write(tmpContent); err != nil {
|
||||||
return *article, err
|
return *article, err
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/mrusme/superhighway84/models"
|
"github.com/mrusme/superhighway84/models"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
var HEADER_LOGO =
|
var HEADER_LOGO =
|
||||||
@ -34,15 +34,6 @@ const (
|
|||||||
COLOR_SUBJECT_READ = "white"
|
COLOR_SUBJECT_READ = "white"
|
||||||
)
|
)
|
||||||
|
|
||||||
var HELP_TEMPLATE =
|
|
||||||
`
|
|
||||||
HELP!
|
|
||||||
|
|
||||||
Default shortcuts:
|
|
||||||
|
|
||||||
%s
|
|
||||||
`
|
|
||||||
|
|
||||||
type GroupMapEntry struct {
|
type GroupMapEntry struct {
|
||||||
Index int
|
Index int
|
||||||
}
|
}
|
||||||
@ -69,8 +60,6 @@ type Mainscreen struct {
|
|||||||
ArticlesList []*models.Article
|
ArticlesList []*models.Article
|
||||||
|
|
||||||
MarkTimer *time.Timer
|
MarkTimer *time.Timer
|
||||||
|
|
||||||
ArticlesListView int8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func(t *TUI) NewMainscreen() (*Mainscreen) {
|
func(t *TUI) NewMainscreen() (*Mainscreen) {
|
||||||
@ -141,31 +130,20 @@ func(t *TUI) NewMainscreen() (*Mainscreen) {
|
|||||||
mainscreen.Footer.SetBorder(false).
|
mainscreen.Footer.SetBorder(false).
|
||||||
SetBorderPadding(0, 0, 1, 1)
|
SetBorderPadding(0, 0, 1, 1)
|
||||||
|
|
||||||
topRowGrid := tview.NewGrid().
|
mainscreen.Canvas = tview.NewGrid().
|
||||||
SetColumns(30, 0, 14).
|
SetRows(5, 0, 0, 1).
|
||||||
AddItem(mainscreen.Header, 0, 0, 1, 2, 0, 0, false).
|
SetColumns(30, 0, 14).
|
||||||
AddItem(mainscreen.Stats, 0, 2, 1, 1, 0, 0, false)
|
SetBorders(false).
|
||||||
|
AddItem(mainscreen.Header, 0, 0, 1, 2, 0, 0, false).
|
||||||
|
AddItem(mainscreen.Stats, 0, 2, 1, 1, 0, 0, false).
|
||||||
|
AddItem(mainscreen.Info, 3, 0, 1, 1, 0, 0, false).
|
||||||
|
AddItem(mainscreen.Footer, 3, 1, 1, 2, 0, 0, false)
|
||||||
|
|
||||||
midRowGrid := tview.NewGrid().
|
mainscreen.Canvas.
|
||||||
SetColumns(-1, -5). // Group takes ~1/5 of the horizontal space available
|
AddItem(mainscreen.Groups, 1, 0, 2, 1, 0, 0, false).
|
||||||
SetRows(-2, -3). // Preview is ~1/3 bigger than Articles
|
AddItem(mainscreen.Articles, 1, 1, 1, 2, 0, 0, false).
|
||||||
AddItem(mainscreen.Groups, 0, 0, 2, 1, 0, 0, false).
|
AddItem(mainscreen.Preview, 2, 1, 1, 2, 0, 0, false)
|
||||||
AddItem(mainscreen.Articles, 0, 1, 1, 1, 0, 0, false).
|
|
||||||
AddItem(mainscreen.Preview, 1, 1, 1, 1, 0, 0, false)
|
|
||||||
|
|
||||||
bottomRowGrid := tview.NewGrid().
|
|
||||||
SetColumns(5, 0, 0).
|
|
||||||
AddItem(mainscreen.Info, 0, 0, 1, 1, 0, 0, false).
|
|
||||||
AddItem(mainscreen.Footer, 0, 1, 1, 2, 0, 0, false)
|
|
||||||
|
|
||||||
mainscreen.Canvas = tview.NewGrid().
|
|
||||||
SetRows(5, 0, 1).
|
|
||||||
SetBorders(false).
|
|
||||||
AddItem(topRowGrid, 0, 0, 1, 1, 0, 0, false).
|
|
||||||
AddItem(midRowGrid, 1, 0, 1, 1, 0, 0, false).
|
|
||||||
AddItem(bottomRowGrid, 2, 0, 1, 1, 0, 0, false)
|
|
||||||
|
|
||||||
mainscreen.ArticlesListView = mainscreen.T.Config.ArticlesListView
|
|
||||||
return mainscreen
|
return mainscreen
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +206,7 @@ func (mainscreen *Mainscreen) GetDefaultFocus() (tview.Primitive) {
|
|||||||
return mainscreen.Articles
|
return mainscreen.Articles
|
||||||
}
|
}
|
||||||
|
|
||||||
func(mainscreen *Mainscreen) addNodeToArticlesList(view int8, level int, articlesNode *[]*models.Article, selectedGroup int, previousGroupsList []string) {
|
func(mainscreen *Mainscreen) addNodeToArticlesList(level int, articlesNode *[]*models.Article, selectedGroup int, previousGroupsList []string) {
|
||||||
// fmt.Fprintf(os.Stderr, "%s Node has %d items\n", strings.Repeat(" ", level * 3), len(*articlesNode))
|
// fmt.Fprintf(os.Stderr, "%s Node has %d items\n", strings.Repeat(" ", level * 3), len(*articlesNode))
|
||||||
|
|
||||||
for i := 0; i < len(*articlesNode); i++ {
|
for i := 0; i < len(*articlesNode); i++ {
|
||||||
@ -241,7 +219,7 @@ func(mainscreen *Mainscreen) addNodeToArticlesList(view int8, level int, article
|
|||||||
article.Newsgroup == previousGroupsList[selectedGroup]) {
|
article.Newsgroup == previousGroupsList[selectedGroup]) {
|
||||||
|
|
||||||
prefix := ""
|
prefix := ""
|
||||||
if view == 0 && level > 0 {
|
if level > 0 {
|
||||||
if i < (len(*articlesNode) - 1) || len(article.Replies) > 0 {
|
if i < (len(*articlesNode) - 1) || len(article.Replies) > 0 {
|
||||||
prefix = "[gray]├[-]"
|
prefix = "[gray]├[-]"
|
||||||
} else {
|
} else {
|
||||||
@ -250,7 +228,7 @@ func(mainscreen *Mainscreen) addNodeToArticlesList(view int8, level int, article
|
|||||||
}
|
}
|
||||||
|
|
||||||
prefixSub := " "
|
prefixSub := " "
|
||||||
if view == 0 && (len(article.Replies) > 0 || (level > 0 && i < (len(*articlesNode) - 1))) {
|
if len(article.Replies) > 0 || (level > 0 && i < (len(*articlesNode) - 1)) {
|
||||||
prefixSub = "[gray]│[-]"
|
prefixSub = "[gray]│[-]"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +254,8 @@ func(mainscreen *Mainscreen) addNodeToArticlesList(view int8, level int, article
|
|||||||
), 0, nil)
|
), 0, nil)
|
||||||
mainscreen.ArticlesList = append(mainscreen.ArticlesList, article)
|
mainscreen.ArticlesList = append(mainscreen.ArticlesList, article)
|
||||||
|
|
||||||
if view == 0 && len(article.Replies) > 0 {
|
if len(article.Replies) > 0 {
|
||||||
mainscreen.addNodeToArticlesList(view, (level + 1), &article.Replies, selectedGroup, previousGroupsList)
|
mainscreen.addNodeToArticlesList((level + 1), &article.Replies, selectedGroup, previousGroupsList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,14 +288,7 @@ func(mainscreen *Mainscreen) Refresh() {
|
|||||||
Index: 0,
|
Index: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
var articlesSource *[]*models.Article
|
mainscreen.addNodeToArticlesList(0, mainscreen.T.ArticlesRoots, selectedGroup, previousGroupsList)
|
||||||
switch(mainscreen.ArticlesListView) {
|
|
||||||
case 0:
|
|
||||||
articlesSource = mainscreen.T.ArticlesRoots
|
|
||||||
case 1:
|
|
||||||
articlesSource = mainscreen.T.ArticlesDatasource
|
|
||||||
}
|
|
||||||
mainscreen.addNodeToArticlesList(mainscreen.ArticlesListView, 0, articlesSource, selectedGroup, previousGroupsList)
|
|
||||||
|
|
||||||
sort.Strings(mainscreen.GroupsList)
|
sort.Strings(mainscreen.GroupsList)
|
||||||
for idx, group := range mainscreen.GroupsList {
|
for idx, group := range mainscreen.GroupsList {
|
||||||
@ -373,8 +344,6 @@ func (mainscreen *Mainscreen) HandleInput(event *tcell.EventKey) (*tcell.EventKe
|
|||||||
mainscreen.T.App.QueueEvent(tcell.NewEventKey(tcell.KeyHome, 0, tcell.ModNone))
|
mainscreen.T.App.QueueEvent(tcell.NewEventKey(tcell.KeyHome, 0, tcell.ModNone))
|
||||||
case "additional-key-end":
|
case "additional-key-end":
|
||||||
mainscreen.T.App.QueueEvent(tcell.NewEventKey(tcell.KeyEnd, 0, tcell.ModNone))
|
mainscreen.T.App.QueueEvent(tcell.NewEventKey(tcell.KeyEnd, 0, tcell.ModNone))
|
||||||
case "help":
|
|
||||||
mainscreen.showHelp()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return event
|
return event
|
||||||
@ -414,19 +383,8 @@ func(mainscreen *Mainscreen) selectHandler(item string)(func(int, string, string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func(mainscreen *Mainscreen) renderPreview(article *models.Article) {
|
func(mainscreen *Mainscreen) renderPreview(article *models.Article) {
|
||||||
var m *regexp.Regexp
|
m := regexp.MustCompile(`(?m)^> (.*)\n`)
|
||||||
body := article.Body
|
body := m.ReplaceAllString(article.Body, "[gray]> $1[-]\n")
|
||||||
|
|
||||||
// Removing GPG/PGP stuff until there is a prober validation for it
|
|
||||||
m = regexp.MustCompile(`(?m)^(> ){0,1}-----BEGIN PGP SIGNED MESSAGE-----\n(> ){0,1}Hash:(.*)(\n( >){0,1}){1,2}`)
|
|
||||||
body = m.ReplaceAllString(body, "")
|
|
||||||
|
|
||||||
m = regexp.MustCompile(`(?sm)^(> ){0,1}-----BEGIN PGP SIGNATURE-----.*-----END PGP SIGNATURE-----$`)
|
|
||||||
body = m.ReplaceAllString(body, "")
|
|
||||||
// End GPG/PGP stuff
|
|
||||||
|
|
||||||
m = regexp.MustCompile(`(?m)^>(.*)(\n){0,1}`)
|
|
||||||
body = m.ReplaceAllString(body, "[gray]> $1[-]\n")
|
|
||||||
|
|
||||||
mainscreen.Preview.SetText(fmt.Sprintf(
|
mainscreen.Preview.SetText(fmt.Sprintf(
|
||||||
"[gray]Date:[-] [darkgray]%s[-]\n[gray]Newsgroup:[-] [darkgray]%s[-]\n\n\n%s",
|
"[gray]Date:[-] [darkgray]%s[-]\n[gray]Newsgroup:[-] [darkgray]%s[-]\n\n\n%s",
|
||||||
@ -559,9 +517,3 @@ func(mainscreen *Mainscreen) replyToArticle(article *models.Article) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func(mainscreen *Mainscreen) showHelp() {
|
|
||||||
helpMessage := fmt.Sprintf(HELP_TEMPLATE, mainscreen.T.Config.ShortcutsReference)
|
|
||||||
mainscreen.T.ShowHelpModal(helpMessage)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"github.com/eliukblau/pixterm/pkg/ansimage"
|
"github.com/eliukblau/pixterm/pkg/ansimage"
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -22,9 +22,9 @@ func(t *TUI) NewSplashscreen(logo *[]byte) (*Splashscreen) {
|
|||||||
splashscreen.T = t
|
splashscreen.T = t
|
||||||
|
|
||||||
canvas := tview.NewTextView().
|
canvas := tview.NewTextView().
|
||||||
SetDynamicColors(true).
|
SetDynamicColors(true).
|
||||||
SetRegions(true).
|
SetRegions(true).
|
||||||
SetWrap(true)
|
SetWrap(true)
|
||||||
canvas.SetBorder(false)
|
canvas.SetBorder(false)
|
||||||
canvas.Clear()
|
canvas.Clear()
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ func(splashscreen *Splashscreen) Refresh() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// splashscreen.Canvas.Clear()
|
// splashscreen.Canvas.Clear()
|
||||||
fmt.Fprint(splashscreen.Canvas, tview.TranslateANSI(logoImage.RenderExt(false, false)))
|
fmt.Fprint(splashscreen.Canvas, tview.TranslateANSI(logoImage.RenderExt(false, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (splashscreen *Splashscreen) HandleInput(event *tcell.EventKey) (*tcell.EventKey) {
|
func (splashscreen *Splashscreen) HandleInput(event *tcell.EventKey) (*tcell.EventKey) {
|
||||||
|
60
tui/tui.go
60
tui/tui.go
@ -1,18 +1,19 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/mrusme/superhighway84/cache"
|
"github.com/mrusme/superhighway84/cache"
|
||||||
"github.com/mrusme/superhighway84/config"
|
"github.com/mrusme/superhighway84/common"
|
||||||
"github.com/mrusme/superhighway84/models"
|
"github.com/mrusme/superhighway84/config"
|
||||||
"github.com/rivo/tview"
|
"github.com/mrusme/superhighway84/models"
|
||||||
"go.uber.org/zap"
|
"github.com/rivo/tview"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TUI struct {
|
type TUI struct {
|
||||||
@ -39,7 +40,8 @@ type TUI struct {
|
|||||||
Version string
|
Version string
|
||||||
VersionLatest string
|
VersionLatest string
|
||||||
|
|
||||||
Meta map[string]interface{}
|
// The fun starts here
|
||||||
|
Player *common.Player
|
||||||
}
|
}
|
||||||
|
|
||||||
type View interface {
|
type View interface {
|
||||||
@ -80,8 +82,6 @@ func Init(embedfs *embed.FS, cfg *config.Config, cch *cache.Cache, logger *zap.L
|
|||||||
|
|
||||||
t.Stats = make(map[string]int64)
|
t.Stats = make(map[string]int64)
|
||||||
|
|
||||||
t.Meta = make(map[string]interface{})
|
|
||||||
|
|
||||||
logoBytes, err := embedfs.ReadFile("superhighway84.jpeg")
|
logoBytes, err := embedfs.ReadFile("superhighway84.jpeg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
@ -95,6 +95,8 @@ func Init(embedfs *embed.FS, cfg *config.Config, cch *cache.Cache, logger *zap.L
|
|||||||
|
|
||||||
t.initInput()
|
t.initInput()
|
||||||
|
|
||||||
|
// The fun stuff
|
||||||
|
t.Player = common.NewPlayer()
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,17 +117,20 @@ func (t *TUI) getInputEvent(event *tcell.EventKey) (string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUI) initInput() {
|
func (t *TUI) initInput() {
|
||||||
t.App.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
t.App.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
action := t.getInputEvent(event)
|
action := t.getInputEvent(event)
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case "refresh":
|
case "refresh":
|
||||||
t.RefreshMainscreen()
|
t.RefreshMainscreen()
|
||||||
t.SetInfo(true)
|
t.SetInfo(true)
|
||||||
t.App.Sync()
|
t.App.Sync()
|
||||||
|
return nil
|
||||||
|
case "quit":
|
||||||
|
t.App.Stop()
|
||||||
return nil
|
return nil
|
||||||
case "quit":
|
case "play":
|
||||||
t.App.Stop()
|
t.Player.Play()
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
if t.ModalVisible == true {
|
if t.ModalVisible == true {
|
||||||
@ -142,8 +147,8 @@ func (t *TUI) initInput() {
|
|||||||
} else {
|
} else {
|
||||||
return t.Views[t.ActiveView].HandleInput(event)
|
return t.Views[t.ActiveView].HandleInput(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TUI) Launch() {
|
func (t *TUI) Launch() {
|
||||||
@ -242,19 +247,6 @@ func(t *TUI) ShowErrorModal(text string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func(t *TUI) ShowHelpModal(text string) {
|
|
||||||
t.ShowModal(
|
|
||||||
text,
|
|
||||||
map[string]ModalButton{
|
|
||||||
"Press any key to close": {
|
|
||||||
Rune: '*',
|
|
||||||
Callback: func() {
|
|
||||||
return
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TUI) SetInfo(refresh bool) {
|
func (t *TUI) SetInfo(refresh bool) {
|
||||||
if refresh == true {
|
if refresh == true {
|
||||||
t.Views["mainscreen"].(*Mainscreen).SetInfo(map[string]string{
|
t.Views["mainscreen"].(*Mainscreen).SetInfo(map[string]string{
|
||||||
|
Loading…
Reference in New Issue
Block a user