Compare commits

...

7 commits

Author SHA1 Message Date
73308d2a32
refactor: Use proberRepo struct instead of interface
o need to use interface when calling `NewProber()`.
2024-07-07 03:25:30 +07:00
4d1a2da49c
refactor: Use moneroRepo struct instead of interface
No need to use interface when calling `monero.New()`.
2024-07-07 03:13:11 +07:00
ef553dab9e
refactor: Use cronRepo struct instead of interface
No need to use interface when calling `cron.New()`.
2024-07-07 02:57:36 +07:00
9a44cd9546
chore: Start v0.0.4 2024-07-07 02:02:03 +07:00
c250e8e3bb
feat!: Moving handler to internal/handler
So people aware that the package is internal use and not using that
on their projects.
2024-07-07 01:42:29 +07:00
4c7d53547b
feat!: Change the module name to "URL based"
This make me easier to test the module with external services.
2024-07-07 01:28:44 +07:00
be43734663
chore(test): Add test shortcut to Makefile
Also rename `.github/workflows/lint.yml` to `.github/workflows/test.yml`
2024-07-07 00:51:54 +07:00
25 changed files with 140 additions and 108 deletions

View file

@ -1,19 +0,0 @@
on:
push:
branches:
- main
pull_request:
name: Test
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.22.x
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest

View file

@ -39,7 +39,7 @@ jobs:
goarch: ${{ matrix.goarch }} goarch: ${{ matrix.goarch }}
overwrite: true overwrite: true
pre_command: export CGO_ENABLED=0 pre_command: export CGO_ENABLED=0
ldflags: -s -w -X xmr-remote-nodes/internal/config.Version=${{github.ref_name}} ldflags: -s -w -X github.com/ditatompel/xmr-remote-nodes/internal/config.Version=${{github.ref_name}}
build_flags: -tags server build_flags: -tags server
project_path: . project_path: .
binary_name: server binary_name: server
@ -53,7 +53,7 @@ jobs:
goarch: ${{ matrix.goarch }} goarch: ${{ matrix.goarch }}
overwrite: true overwrite: true
pre_command: export CGO_ENABLED=0 pre_command: export CGO_ENABLED=0
ldflags: -s -w -X xmr-remote-nodes/internal/config.Version=${{github.ref_name}} ldflags: -s -w -X github.com/ditatompel/xmr-remote-nodes/internal/config.Version=${{github.ref_name}}
binary_name: client binary_name: client
project_path: . project_path: .
extra_files: LICENSE README.md extra_files: LICENSE README.md

51
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,51 @@
on:
push:
branches:
- main
pull_request:
name: Test
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.22.x
- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
- name: setup NodeJS
uses: actions/setup-node@v4
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
- name: Cache node modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Build UI
run: make ui
- name: Run test
run: make test

View file

@ -27,7 +27,7 @@ ifdef RELEASE_TAG
endif endif
# end modified rclone's Makefile # end modified rclone's Makefile
BUILD_LDFLAGS := -s -w -X xmr-remote-nodes/internal/config.Version=$(TAG) BUILD_LDFLAGS := -s -w -X github.com/ditatompel/xmr-remote-nodes/internal/config.Version=$(TAG)
# This called from air cmd (see .air.toml) # This called from air cmd (see .air.toml)
.PHONY: dev .PHONY: dev
@ -65,6 +65,18 @@ clean:
rm -rfv ./bin rm -rfv ./bin
rm -rf ./frontend/build rm -rf ./frontend/build
.PHONY: lint
lint:
golangci-lint run ./...
.PHONY: test
test:
go test -race -cover ./...
.PHONY: bench
bench:
go test ./... -bench=. -benchmem -run=^#
# Deploying new binary file to server and probers host # Deploying new binary file to server and probers host
# The deploy-* command doesn't build the binary file, so you need to run `make build` first. # The deploy-* command doesn't build the binary file, so you need to run `make build` first.
# And make sure the inventory and deploy-*.yml file is properly configured. # And make sure the inventory and deploy-*.yml file is properly configured.

View file

@ -1,6 +1,6 @@
# XMR Remote Nodes # XMR Remote Nodes
[![Lint](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/lint.yml/badge.svg)](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/lint.yml) [![Test](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/test.yml/badge.svg)](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/test.yml)
[![BUild](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/build.yml/badge.svg)](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/build.yml) [![BUild](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/build.yml/badge.svg)](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/build.yml)
[![Release Binaries](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/release.yml/badge.svg)](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/release.yml) [![Release Binaries](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/release.yml/badge.svg)](https://github.com/ditatompel/xmr-remote-nodes/actions/workflows/release.yml)

View file

@ -1 +1 @@
v0.0.3 v0.0.4

View file

@ -12,8 +12,9 @@ import (
"net/http" "net/http"
"os" "os"
"time" "time"
"xmr-remote-nodes/internal/config"
"xmr-remote-nodes/internal/monero" "github.com/ditatompel/xmr-remote-nodes/internal/config"
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/net/proxy" "golang.org/x/net/proxy"

View file

@ -2,8 +2,9 @@ package cmd
import ( import (
"os" "os"
"xmr-remote-nodes/cmd/client"
"xmr-remote-nodes/internal/config" "github.com/ditatompel/xmr-remote-nodes/cmd/client"
"github.com/ditatompel/xmr-remote-nodes/internal/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )

View file

@ -5,8 +5,9 @@ import (
"os" "os"
"text/tabwriter" "text/tabwriter"
"time" "time"
"xmr-remote-nodes/internal/cron"
"xmr-remote-nodes/internal/database" "github.com/ditatompel/xmr-remote-nodes/internal/cron"
"github.com/ditatompel/xmr-remote-nodes/internal/database"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )

View file

@ -1,6 +1,6 @@
package server package server
import "xmr-remote-nodes/cmd" import "github.com/ditatompel/xmr-remote-nodes/cmd"
func init() { func init() {
cmd.Root.AddCommand(serveCmd) cmd.Root.AddCommand(serveCmd)

View file

@ -9,8 +9,9 @@ import (
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"time" "time"
"xmr-remote-nodes/internal/database"
"xmr-remote-nodes/internal/monero" "github.com/ditatompel/xmr-remote-nodes/internal/database"
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )

View file

@ -7,11 +7,12 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"time" "time"
"xmr-remote-nodes/frontend"
"xmr-remote-nodes/handler" "github.com/ditatompel/xmr-remote-nodes/frontend"
"xmr-remote-nodes/internal/config" "github.com/ditatompel/xmr-remote-nodes/internal/config"
"xmr-remote-nodes/internal/cron" "github.com/ditatompel/xmr-remote-nodes/internal/cron"
"xmr-remote-nodes/internal/database" "github.com/ditatompel/xmr-remote-nodes/internal/database"
"github.com/ditatompel/xmr-remote-nodes/internal/handler"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/cors"

View file

@ -1,6 +1,6 @@
{ {
"name": "xmr-nodes-frontend", "name": "xmr-nodes-frontend",
"version": "v0.0.3", "version": "v0.0.4",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "VITE_API_URL=http://127.0.0.1:18901 vite dev --host 127.0.0.1", "dev": "VITE_API_URL=http://127.0.0.1:18901 vite dev --host 127.0.0.1",

2
go.mod
View file

@ -1,4 +1,4 @@
module xmr-remote-nodes module github.com/ditatompel/xmr-remote-nodes
go 1.22.2 go 1.22.2

View file

@ -5,15 +5,11 @@ import (
"log/slog" "log/slog"
"math" "math"
"time" "time"
"xmr-remote-nodes/internal/database"
"github.com/ditatompel/xmr-remote-nodes/internal/database"
) )
type CronRepository interface { type cronRepo struct {
RunCronProcess(chan struct{})
Crons() ([]Cron, error)
}
type CronRepo struct {
db *database.DB db *database.DB
} }
@ -32,11 +28,11 @@ type Cron struct {
var rerunTimeout = 300 var rerunTimeout = 300
func New() CronRepository { func New() *cronRepo {
return &CronRepo{db: database.GetDB()} return &cronRepo{db: database.GetDB()}
} }
func (r *CronRepo) RunCronProcess(c chan struct{}) { func (r *cronRepo) RunCronProcess(c chan struct{}) {
for { for {
select { select {
case <-time.After(60 * time.Second): case <-time.After(60 * time.Second):
@ -72,7 +68,7 @@ func (r *CronRepo) RunCronProcess(c chan struct{}) {
} }
} }
func (r *CronRepo) Crons() ([]Cron, error) { func (r *cronRepo) Crons() ([]Cron, error) {
var tasks []Cron var tasks []Cron
err := r.db.Select(&tasks, ` err := r.db.Select(&tasks, `
SELECT SELECT
@ -91,7 +87,7 @@ func (r *CronRepo) Crons() ([]Cron, error) {
return tasks, err return tasks, err
} }
func (r *CronRepo) queueList() ([]Cron, error) { func (r *cronRepo) queueList() ([]Cron, error) {
tasks := []Cron{} tasks := []Cron{}
query := ` query := `
SELECT SELECT
@ -111,7 +107,7 @@ func (r *CronRepo) queueList() ([]Cron, error) {
return tasks, err return tasks, err
} }
func (r *CronRepo) preRunTask(id int, lastRunTs int64) { func (r *cronRepo) preRunTask(id int, lastRunTs int64) {
query := ` query := `
UPDATE tbl_cron UPDATE tbl_cron
SET SET
@ -126,7 +122,7 @@ func (r *CronRepo) preRunTask(id int, lastRunTs int64) {
defer row.Close() defer row.Close()
} }
func (r *CronRepo) postRunTask(id int, nextRun int64, runtime float64) { func (r *cronRepo) postRunTask(id int, nextRun int64, runtime float64) {
query := ` query := `
UPDATE tbl_cron UPDATE tbl_cron
SET SET
@ -142,7 +138,7 @@ func (r *CronRepo) postRunTask(id int, nextRun int64, runtime float64) {
defer row.Close() defer row.Close()
} }
func (r *CronRepo) execCron(slug string) { func (r *cronRepo) execCron(slug string) {
switch slug { switch slug {
case "delete_old_probe_logs": case "delete_old_probe_logs":
slog.Info(fmt.Sprintf("[CRON] Start running task: %s", slug)) slog.Info(fmt.Sprintf("[CRON] Start running task: %s", slug))
@ -153,7 +149,7 @@ func (r *CronRepo) execCron(slug string) {
} }
} }
func (r *CronRepo) deleteOldProbeLogs() { func (r *cronRepo) deleteOldProbeLogs() {
// for now, we only delete stats older than 1 month +2 days // for now, we only delete stats older than 1 month +2 days
startTs := time.Now().AddDate(0, -1, -2).Unix() startTs := time.Now().AddDate(0, -1, -2).Unix()
query := `DELETE FROM tbl_probe_log WHERE date_checked < ?` query := `DELETE FROM tbl_probe_log WHERE date_checked < ?`
@ -163,7 +159,7 @@ func (r *CronRepo) deleteOldProbeLogs() {
} }
} }
func (r *CronRepo) calculateMajorityFee() { func (r *cronRepo) calculateMajorityFee() {
netTypes := [3]string{"mainnet", "stagenet", "testnet"} netTypes := [3]string{"mainnet", "stagenet", "testnet"}
for _, net := range netTypes { for _, net := range netTypes {
row, err := r.db.Query(` row, err := r.db.Query(`

View file

@ -2,7 +2,8 @@ package database
import ( import (
"fmt" "fmt"
"xmr-remote-nodes/internal/config"
"github.com/ditatompel/xmr-remote-nodes/internal/config"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"

View file

@ -1,7 +1,7 @@
package handler package handler
import ( import (
"xmr-remote-nodes/internal/monero" "github.com/ditatompel/xmr-remote-nodes/internal/monero"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )

View file

@ -2,7 +2,8 @@ package handler
import ( import (
"strconv" "strconv"
"xmr-remote-nodes/internal/monero"
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )

View file

@ -10,28 +10,18 @@ import (
"slices" "slices"
"strings" "strings"
"time" "time"
"xmr-remote-nodes/internal/database"
"github.com/ditatompel/xmr-remote-nodes/internal/database"
"github.com/jmoiron/sqlx/types" "github.com/jmoiron/sqlx/types"
) )
type MoneroRepository interface { type moneroRepo struct {
Node(id int) (Node, error)
Add(protocol string, host string, port uint) error
Nodes(QueryNodes) (Nodes, error)
NetFees() []NetFee
Countries() ([]Countries, error)
GiveJob(acceptTor int) (Node, error)
ProcessJob(report ProbeReport, proberId int64) error
Logs(QueryLogs) (FetchLogs, error)
}
type MoneroRepo struct {
db *database.DB db *database.DB
} }
func New() MoneroRepository { func New() *moneroRepo {
return &MoneroRepo{db: database.GetDB()} return &moneroRepo{db: database.GetDB()}
} }
// Node represents a single remote node // Node represents a single remote node
@ -67,7 +57,7 @@ type Node struct {
} }
// Get node from database by id // Get node from database by id
func (r *MoneroRepo) Node(id int) (Node, error) { func (r *moneroRepo) Node(id int) (Node, error) {
var node Node var node Node
err := r.db.Get(&node, `SELECT * FROM tbl_node WHERE id = ?`, id) err := r.db.Get(&node, `SELECT * FROM tbl_node WHERE id = ?`, id)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
@ -161,7 +151,7 @@ type Nodes struct {
} }
// Get nodes from database // Get nodes from database
func (r *MoneroRepo) Nodes(q QueryNodes) (Nodes, error) { func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
args, where, sortBy, sortDirection := q.toSQL() args, where, sortBy, sortDirection := q.toSQL()
var nodes Nodes var nodes Nodes
@ -197,7 +187,7 @@ func (r *MoneroRepo) Nodes(q QueryNodes) (Nodes, error) {
return nodes, err return nodes, err
} }
func (repo *MoneroRepo) Add(protocol string, hostname string, port uint) error { func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
if protocol != "http" && protocol != "https" { if protocol != "http" && protocol != "https" {
return errors.New("Invalid protocol, must one of or HTTP/HTTPS") return errors.New("Invalid protocol, must one of or HTTP/HTTPS")
} }
@ -232,7 +222,7 @@ func (repo *MoneroRepo) Add(protocol string, hostname string, port uint) error {
ip = hostIp.String() ip = hostIp.String()
} }
row, err := repo.db.Query(` row, err := r.db.Query(`
SELECT SELECT
id id
FROM FROM
@ -251,7 +241,7 @@ func (repo *MoneroRepo) Add(protocol string, hostname string, port uint) error {
return errors.New("Node already monitored") return errors.New("Node already monitored")
} }
statusDb, _ := json.Marshal([5]int{2, 2, 2, 2, 2}) statusDb, _ := json.Marshal([5]int{2, 2, 2, 2, 2})
_, err = repo.db.Exec(` _, err = r.db.Exec(`
INSERT INTO tbl_node ( INSERT INTO tbl_node (
protocol, protocol,
hostname, hostname,
@ -295,7 +285,7 @@ func (repo *MoneroRepo) Add(protocol string, hostname string, port uint) error {
return nil return nil
} }
func (r *MoneroRepo) Delete(id uint) error { func (r *moneroRepo) Delete(id uint) error {
if _, err := r.db.Exec(`DELETE FROM tbl_node WHERE id = ?`, id); err != nil { if _, err := r.db.Exec(`DELETE FROM tbl_node WHERE id = ?`, id); err != nil {
return err return err
} }
@ -313,7 +303,7 @@ type NetFee struct {
} }
// Get majority net fee from table tbl_fee // Get majority net fee from table tbl_fee
func (r *MoneroRepo) NetFees() []NetFee { func (r *moneroRepo) NetFees() []NetFee {
var netFees []NetFee var netFees []NetFee
err := r.db.Select(&netFees, ` err := r.db.Select(&netFees, `
SELECT SELECT
@ -337,7 +327,7 @@ type Countries struct {
} }
// Get list of countries (count by nodes) // Get list of countries (count by nodes)
func (r *MoneroRepo) Countries() ([]Countries, error) { func (r *moneroRepo) Countries() ([]Countries, error) {
var c []Countries var c []Countries
err := r.db.Select(&c, ` err := r.db.Select(&c, `
SELECT SELECT

View file

@ -5,8 +5,9 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"testing" "testing"
"xmr-remote-nodes/internal/config"
"xmr-remote-nodes/internal/database" "github.com/ditatompel/xmr-remote-nodes/internal/config"
"github.com/ditatompel/xmr-remote-nodes/internal/database"
) )
var testMySQL = true var testMySQL = true

View file

@ -4,22 +4,15 @@ import (
"fmt" "fmt"
"slices" "slices"
"strings" "strings"
"xmr-remote-nodes/internal/database"
"github.com/ditatompel/xmr-remote-nodes/internal/database"
"github.com/google/uuid" "github.com/google/uuid"
) )
const ProberAPIKey = "X-Prober-Api-Key" // HTTP header key const ProberAPIKey = "X-Prober-Api-Key" // HTTP header key
type ProberRepository interface { type proberRepo struct {
Add(name string) (Prober, error)
Edit(id int, name string) error
Probers(QueryProbers) ([]Prober, error)
CheckAPI(key string) (Prober, error)
Delete(id int) error
}
type ProberRepo struct {
db *database.DB db *database.DB
} }
@ -33,12 +26,12 @@ type Prober struct {
// Initializes a new ProberRepository // Initializes a new ProberRepository
// //
// NOTE: This "prober" is different with "probe" which is used to fetch a new job // NOTE: This "prober" is different with "probe" which is used to fetch a new job
func NewProber() ProberRepository { func NewProber() *proberRepo {
return &ProberRepo{db: database.GetDB()} return &proberRepo{db: database.GetDB()}
} }
// Add a new prober machine // Add a new prober machine
func (r *ProberRepo) Add(name string) (Prober, error) { func (r *proberRepo) Add(name string) (Prober, error) {
apiKey := uuid.New() apiKey := uuid.New()
query := ` query := `
INSERT INTO tbl_prober ( INSERT INTO tbl_prober (
@ -58,7 +51,7 @@ func (r *ProberRepo) Add(name string) (Prober, error) {
} }
// Edit an existing prober // Edit an existing prober
func (r *ProberRepo) Edit(id int, name string) error { func (r *proberRepo) Edit(id int, name string) error {
query := `UPDATE tbl_prober SET name = ? WHERE id = ?` query := `UPDATE tbl_prober SET name = ? WHERE id = ?`
res, err := r.db.Exec(query, name, id) res, err := r.db.Exec(query, name, id)
if err != nil { if err != nil {
@ -75,7 +68,7 @@ func (r *ProberRepo) Edit(id int, name string) error {
} }
// Delete an existing prober // Delete an existing prober
func (r *ProberRepo) Delete(id int) error { func (r *proberRepo) Delete(id int) error {
res, err := r.db.Exec(`DELETE FROM tbl_prober WHERE id = ?`, id) res, err := r.db.Exec(`DELETE FROM tbl_prober WHERE id = ?`, id)
if err != nil { if err != nil {
return err return err
@ -119,7 +112,7 @@ func (q QueryProbers) toSQL() (args []interface{}, where, sortBy, sortDirection
return args, where, sortBy, sortDirection return args, where, sortBy, sortDirection
} }
func (r *ProberRepo) Probers(q QueryProbers) ([]Prober, error) { func (r *proberRepo) Probers(q QueryProbers) ([]Prober, error) {
args, where, sortBy, sortDirection := q.toSQL() args, where, sortBy, sortDirection := q.toSQL()
var probers []Prober var probers []Prober
@ -152,7 +145,7 @@ func (r *ProberRepo) Probers(q QueryProbers) ([]Prober, error) {
return probers, nil return probers, nil
} }
func (r *ProberRepo) CheckAPI(key string) (Prober, error) { func (r *proberRepo) CheckAPI(key string) (Prober, error) {
var p Prober var p Prober
query := ` query := `
SELECT SELECT

View file

@ -9,7 +9,8 @@ import (
"slices" "slices"
"strings" "strings"
"time" "time"
"xmr-remote-nodes/internal/geo"
"github.com/ditatompel/xmr-remote-nodes/internal/geo"
) )
type QueryLogs struct { type QueryLogs struct {
@ -77,7 +78,7 @@ type FetchLogs struct {
} }
// Logs returns list of fetched log result for given query // Logs returns list of fetched log result for given query
func (r *MoneroRepo) Logs(q QueryLogs) (FetchLogs, error) { func (r *moneroRepo) Logs(q QueryLogs) (FetchLogs, error) {
args, where, sortBy, sortDirection := q.toSQL() args, where, sortBy, sortDirection := q.toSQL()
var fetchLogs FetchLogs var fetchLogs FetchLogs
@ -107,7 +108,7 @@ func (r *MoneroRepo) Logs(q QueryLogs) (FetchLogs, error) {
} }
// GiveJob returns node that should be probed for the next time // GiveJob returns node that should be probed for the next time
func (r *MoneroRepo) GiveJob(acceptTor int) (Node, error) { func (r *moneroRepo) GiveJob(acceptTor int) (Node, error) {
args := []interface{}{} args := []interface{}{}
wq := []string{} wq := []string{}
where := "" where := ""
@ -195,7 +196,7 @@ func (p *ProbeReport) parseStatuses() string {
} }
// Process report data from probers // Process report data from probers
func (r *MoneroRepo) ProcessJob(report ProbeReport, proberId int64) error { func (r *moneroRepo) ProcessJob(report ProbeReport, proberId int64) error {
if report.Node.ID == 0 { if report.Node.ID == 0 {
return errors.New("Invalid node") return errors.New("Invalid node")
} }

View file

@ -1,7 +1,7 @@
package main package main
import ( import (
"xmr-remote-nodes/cmd" "github.com/ditatompel/xmr-remote-nodes/cmd"
) )
func main() { func main() {

View file

@ -2,4 +2,4 @@
package main package main
import _ "xmr-remote-nodes/cmd/server" import _ "github.com/ditatompel/xmr-remote-nodes/cmd/server"