mirror of
https://github.com/mrusme/neonmodem.git
synced 2024-12-04 14:46:37 -05:00
Extended discourse implementation
This commit is contained in:
parent
49205c4b7f
commit
a34fa46c3d
200
system/discourse/client.go
Normal file
200
system/discourse/client.go
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
package discourse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-retryablehttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Validation map[string]interface{} `json:"validation,omitempty"`
|
||||||
|
|
||||||
|
Post PostModel `json:"post,omitempty"`
|
||||||
|
Posts []PostModel `json:"latest_posts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestError struct {
|
||||||
|
Response Response
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *RequestError) Error() string {
|
||||||
|
return re.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *RequestError) Type() string {
|
||||||
|
return re.Response.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *RequestError) Message() string {
|
||||||
|
return re.Response.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
type Logger interface {
|
||||||
|
Debug(args ...interface{})
|
||||||
|
Debugf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Info(args ...interface{})
|
||||||
|
Infof(format string, args ...interface{})
|
||||||
|
|
||||||
|
Warn(args ...interface{})
|
||||||
|
Warnf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Error(args ...interface{})
|
||||||
|
Errorf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type StdLogger struct {
|
||||||
|
L Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStdLogger(l Logger) retryablehttp.Logger {
|
||||||
|
return &StdLogger{
|
||||||
|
L: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *StdLogger) Printf(message string, v ...interface{}) {
|
||||||
|
l.L.Debug(message, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
Endpoint string
|
||||||
|
Credentials map[string]string
|
||||||
|
HTTPClient *http.Client
|
||||||
|
Logger Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
httpClient *retryablehttp.Client
|
||||||
|
endpoint *url.URL
|
||||||
|
credentials map[string]string
|
||||||
|
logger Logger
|
||||||
|
Posts PostsService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultClientConfig(
|
||||||
|
endpoint string,
|
||||||
|
credentials map[string]string,
|
||||||
|
logger Logger,
|
||||||
|
) ClientConfig {
|
||||||
|
return ClientConfig{
|
||||||
|
Endpoint: endpoint,
|
||||||
|
Credentials: credentials,
|
||||||
|
HTTPClient: http.DefaultClient,
|
||||||
|
Logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(cc *ClientConfig) *Client {
|
||||||
|
c := new(Client)
|
||||||
|
c.logger = cc.Logger
|
||||||
|
c.httpClient = retryablehttp.NewClient()
|
||||||
|
c.httpClient.RetryMax = 3
|
||||||
|
if c.logger != nil {
|
||||||
|
c.httpClient.Logger = NewStdLogger(c.logger)
|
||||||
|
}
|
||||||
|
c.httpClient.HTTPClient = cc.HTTPClient
|
||||||
|
c.endpoint, _ = url.Parse(cc.Endpoint)
|
||||||
|
c.credentials = cc.Credentials
|
||||||
|
fmt.Printf("%v\n", c.credentials)
|
||||||
|
|
||||||
|
c.Posts = &PostServiceHandler{client: c}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) NewRequest(
|
||||||
|
ctx context.Context,
|
||||||
|
method string,
|
||||||
|
location string,
|
||||||
|
body interface{},
|
||||||
|
) (*http.Request, error) {
|
||||||
|
var parsedURL *url.URL
|
||||||
|
var req *http.Request
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if parsedURL, err = c.endpoint.Parse(location); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
if body != nil {
|
||||||
|
if err = json.NewEncoder(buffer).Encode(body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req, err = http.NewRequest(
|
||||||
|
method,
|
||||||
|
parsedURL.String(),
|
||||||
|
buffer,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("User-Agent", "gobbs")
|
||||||
|
req.Header.Add("Accept", "application/json")
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
req.Header.Add("Api-Username", c.credentials["username"])
|
||||||
|
req.Header.Add("Api-Key", c.credentials["key"])
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", req)
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Do(
|
||||||
|
ctx context.Context,
|
||||||
|
req *http.Request,
|
||||||
|
content *Response,
|
||||||
|
) error {
|
||||||
|
var rreq *retryablehttp.Request
|
||||||
|
var res *http.Response
|
||||||
|
var body []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if rreq, err = retryablehttp.FromRequest(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rreq = rreq.WithContext(ctx)
|
||||||
|
if res, err = c.httpClient.Do(rreq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if body, err = ioutil.ReadAll(res.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if content != nil {
|
||||||
|
if err = json.Unmarshal(body, content); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", res)
|
||||||
|
|
||||||
|
if res.StatusCode < http.StatusOK ||
|
||||||
|
res.StatusCode > http.StatusNoContent {
|
||||||
|
return &RequestError{
|
||||||
|
Err: errors.New("Non-2xx status code"),
|
||||||
|
Response: *content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -2,6 +2,7 @@ package discourse
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -17,10 +19,12 @@ import (
|
|||||||
"github.com/mrusme/gobbs/models/post"
|
"github.com/mrusme/gobbs/models/post"
|
||||||
"github.com/mrusme/gobbs/system/adapter"
|
"github.com/mrusme/gobbs/system/adapter"
|
||||||
"github.com/pkg/browser"
|
"github.com/pkg/browser"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type System struct {
|
type System struct {
|
||||||
config map[string]interface{}
|
config map[string]interface{}
|
||||||
|
logger *zap.SugaredLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserAPIKey struct {
|
type UserAPIKey struct {
|
||||||
@ -38,6 +42,10 @@ func (sys *System) SetConfig(cfg *map[string]interface{}) {
|
|||||||
sys.config = *cfg
|
sys.config = *cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sys *System) SetLogger(logger *zap.SugaredLogger) {
|
||||||
|
sys.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
func (sys *System) Load() error {
|
func (sys *System) Load() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -149,12 +157,31 @@ func (sys *System) Connect(sysURL string) error {
|
|||||||
if sys.config == nil {
|
if sys.config == nil {
|
||||||
sys.config = make(map[string]interface{})
|
sys.config = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
sys.config["url"] = sysURL
|
||||||
sys.config["credentials"] = credentials
|
sys.config["credentials"] = credentials
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sys *System) ListPosts() ([]post.Post, error) {
|
func (sys *System) ListPosts() ([]post.Post, error) {
|
||||||
|
credentials := make(map[string]string)
|
||||||
|
for k, v := range (sys.config["credentials"]).(map[string]interface{}) {
|
||||||
|
credentials[k] = v.(string)
|
||||||
|
}
|
||||||
|
c := NewClient(&ClientConfig{
|
||||||
|
Endpoint: sys.config["url"].(string),
|
||||||
|
Credentials: credentials,
|
||||||
|
HTTPClient: http.DefaultClient,
|
||||||
|
Logger: sys.logger,
|
||||||
|
})
|
||||||
|
|
||||||
|
posts, err := c.Posts.List(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return []post.Post{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", posts)
|
||||||
|
|
||||||
return []post.Post{}, nil
|
return []post.Post{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
115
system/discourse/posts.go
Normal file
115
system/discourse/posts.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package discourse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PostsBaseURL = "/posts"
|
||||||
|
|
||||||
|
type PostModel struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
AvatarTemplate string `json:"avater_template"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
Cooked string `json:"cooked"`
|
||||||
|
PostNumber int `json:"post_number"`
|
||||||
|
PostType int `json:"post_type"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
ReplyCount int `json:"reply_count"`
|
||||||
|
IncomingLinkCount int `json:"incoming_link_count"`
|
||||||
|
Reads int `json:"reads"`
|
||||||
|
ReadersCount int `json:"readers_count"`
|
||||||
|
Score int `json:"score"`
|
||||||
|
Yours bool `json:"yours"`
|
||||||
|
TopicID int `json:"topic_id"`
|
||||||
|
TopicSlug string `json:"topic_slug"`
|
||||||
|
TopicTitle string `json:"topic_title"`
|
||||||
|
TopicHTMLTitle string `json:"topic_html_title"`
|
||||||
|
CategoryID int `json:"category_id"`
|
||||||
|
DisplayUsername string `json:"display_username"`
|
||||||
|
PrimaryGroupName string `json:"primary_group_name"`
|
||||||
|
FlairName string `json:"flair_name"`
|
||||||
|
FlairURL string `json:"flair_url"`
|
||||||
|
FlairBGColor string `json:"flair_bg_color"`
|
||||||
|
FlairColor string `json:"flair_color"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
CanEdit bool `json:"can_edit"`
|
||||||
|
CanDelete bool `json:"can_delete"`
|
||||||
|
CanRecover bool `json:"can_recover"`
|
||||||
|
CanWiki bool `json:"can_wiki"`
|
||||||
|
UserTitle string `json:"user_title"`
|
||||||
|
Raw string `json:"raw"`
|
||||||
|
ActionsSummary struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
CanAct bool `json:"can_act"`
|
||||||
|
} `json:"actions_summary"`
|
||||||
|
Moderator bool `json:"moderator"`
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
Staff bool `json:"staff"`
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
Hidden bool `json:"hidden"`
|
||||||
|
TrustLevel int `json:"trust_level"`
|
||||||
|
DeletedAt string `json:"deleted_at"`
|
||||||
|
UserDeleted bool `json:"user_deleted"`
|
||||||
|
EditReason string `json:"edit_reason"`
|
||||||
|
CanViewEditHistory bool `json:"can_view_edit_history"`
|
||||||
|
Wiki bool `json:"wiki"`
|
||||||
|
ReviewableID string `json:"reviewable_id"`
|
||||||
|
ReviewableScoreCount int `json:"reviewable_score_count"`
|
||||||
|
ReviewableScorePendingCount int `json:"reviewable_score_pending_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostsService interface {
|
||||||
|
Show(
|
||||||
|
ctx context.Context,
|
||||||
|
id string,
|
||||||
|
) (PostModel, error)
|
||||||
|
List(
|
||||||
|
ctx context.Context,
|
||||||
|
) ([]PostModel, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostServiceHandler struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show
|
||||||
|
func (a *PostServiceHandler) Show(
|
||||||
|
ctx context.Context,
|
||||||
|
id string,
|
||||||
|
) (PostModel, error) {
|
||||||
|
uri := PostsBaseURL + "/" + id + ".json"
|
||||||
|
|
||||||
|
req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return PostModel{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := new(Response)
|
||||||
|
if err = a.client.Do(ctx, req, response); err != nil {
|
||||||
|
return PostModel{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Post, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List
|
||||||
|
func (a *PostServiceHandler) List(
|
||||||
|
ctx context.Context,
|
||||||
|
) ([]PostModel, error) {
|
||||||
|
uri := PostsBaseURL + ".json"
|
||||||
|
|
||||||
|
req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []PostModel{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := new(Response)
|
||||||
|
if err = a.client.Do(ctx, req, response); err != nil {
|
||||||
|
return []PostModel{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Posts, nil
|
||||||
|
}
|
@ -3,10 +3,12 @@ package lemmy
|
|||||||
import (
|
import (
|
||||||
"github.com/mrusme/gobbs/models/post"
|
"github.com/mrusme/gobbs/models/post"
|
||||||
"github.com/mrusme/gobbs/system/adapter"
|
"github.com/mrusme/gobbs/system/adapter"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type System struct {
|
type System struct {
|
||||||
config map[string]interface{}
|
config map[string]interface{}
|
||||||
|
logger *zap.SugaredLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sys *System) GetConfig() map[string]interface{} {
|
func (sys *System) GetConfig() map[string]interface{} {
|
||||||
@ -17,6 +19,10 @@ func (sys *System) SetConfig(cfg *map[string]interface{}) {
|
|||||||
sys.config = *cfg
|
sys.config = *cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sys *System) SetLogger(logger *zap.SugaredLogger) {
|
||||||
|
sys.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
func (sys *System) Connect(sysURL string) error {
|
func (sys *System) Connect(sysURL string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,13 @@ import (
|
|||||||
"github.com/mrusme/gobbs/system/adapter"
|
"github.com/mrusme/gobbs/system/adapter"
|
||||||
"github.com/mrusme/gobbs/system/discourse"
|
"github.com/mrusme/gobbs/system/discourse"
|
||||||
"github.com/mrusme/gobbs/system/lemmy"
|
"github.com/mrusme/gobbs/system/lemmy"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type System interface {
|
type System interface {
|
||||||
GetConfig() map[string]interface{}
|
GetConfig() map[string]interface{}
|
||||||
SetConfig(cfg *map[string]interface{})
|
SetConfig(cfg *map[string]interface{})
|
||||||
|
SetLogger(logger *zap.SugaredLogger)
|
||||||
GetCapabilities() []adapter.Capability
|
GetCapabilities() []adapter.Capability
|
||||||
|
|
||||||
Connect(sysURL string) error
|
Connect(sysURL string) error
|
||||||
@ -20,7 +22,11 @@ type System interface {
|
|||||||
ListPosts() ([]post.Post, error)
|
ListPosts() ([]post.Post, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(sysType string, sysConfig *map[string]interface{}) (System, error) {
|
func New(
|
||||||
|
sysType string,
|
||||||
|
sysConfig *map[string]interface{},
|
||||||
|
logger *zap.SugaredLogger,
|
||||||
|
) (System, error) {
|
||||||
var sys System
|
var sys System
|
||||||
|
|
||||||
switch sysType {
|
switch sysType {
|
||||||
@ -33,6 +39,7 @@ func New(sysType string, sysConfig *map[string]interface{}) (System, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sys.SetConfig(sysConfig)
|
sys.SetConfig(sysConfig)
|
||||||
|
sys.SetLogger(logger)
|
||||||
err := sys.Load()
|
err := sys.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user