From 935fc33094aa54641714022ec757280f43734572 Mon Sep 17 00:00:00 2001 From: mStar aka a person <12024604-mstarongitlab@users.noreply.gitlab.com> Date: Fri, 19 Jan 2024 10:21:31 +0000 Subject: [PATCH] Uuuh, lots of stuff More config, moved files, some endpoints (wip), some storage --- .devcontainer/Dockerfile | 13 +++ .devcontainer/devcontainer.json | 23 ++++++ .devcontainer/docker-compose.yml | 38 +++++++++ config.toml | 4 +- config/config.go | 11 ++- endpoints/api/ap/ap.go | 26 ++++++ endpoints/api/ap/webfinger.go | 91 +++++++++++++++++++++ objects/person.go => endpoints/endpoints.go | 10 ++- go.mod | 7 +- go.sum | 10 +++ main.go | 8 +- server/server.go | 13 +++ storage/db_types.go | 59 ------------- objects/objects.go => storage/follows.go | 17 +++- storage/note.go | 37 +++++++++ storage/person.go | 59 +++++++++++++ storage/storage.go | 30 ++++++- types/types.go | 8 ++ 18 files changed, 394 insertions(+), 70 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 endpoints/api/ap/ap.go create mode 100644 endpoints/api/ap/webfinger.go rename objects/person.go => endpoints/endpoints.go (69%) create mode 100644 server/server.go delete mode 100644 storage/db_types.go rename objects/objects.go => storage/follows.go (51%) create mode 100644 storage/note.go create mode 100644 storage/person.go create mode 100644 types/types.go diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..1b397b3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,13 @@ +FROM mcr.microsoft.com/devcontainers/go:1-1.21-bullseye + +# [Optional] Uncomment this section to install additional OS packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends postgresql-client + +# [Optional] Uncomment the next lines to use go get to install anything else you need +# USER vscode +# RUN go get -x +# USER root + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..816ba3c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/go-postgres +{ + "name": "Go & PostgreSQL", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}" + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Configure tool-specific properties. + // "customizations": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [5432], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "go version", + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..071a939 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.8' + +volumes: + postgres-data: + +services: + app: + build: + context: . + dockerfile: Dockerfile + env_file: + # Ensure that the variables in .env match the same variables in devcontainer.json + - .env + + volumes: + - ../..:/workspaces:cached + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + + # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + db: + image: postgres:latest + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + env_file: + # Ensure that the variables in .env match the same variables in devcontainer.json + - .env + + + # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) diff --git a/config.toml b/config.toml index 0c74534..10b0f55 100644 --- a/config.toml +++ b/config.toml @@ -1,2 +1,4 @@ [General] -database_path="postgres://linstrom:ugachaka@localhost/linstrom" \ No newline at end of file +database_path="postgres://postgres:postgres@localhost/postgres" +handle_ssl=true +enable_ui=true \ No newline at end of file diff --git a/config/config.go b/config/config.go index c651d7a..d18f285 100644 --- a/config/config.go +++ b/config/config.go @@ -15,6 +15,7 @@ import ( "os" "github.com/BurntSushi/toml" + "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" log "github.com/sirupsen/logrus" ) @@ -29,7 +30,13 @@ type CLIArguments struct { type Config struct { General struct { - DbPath string `envconfig:"database" toml:"database_path"` + // Path to the postgres db + DbPath string `envconfig:"DATABASE_PATH" toml:"database_path"` + // Whether the server should handle ssl itself + HandleSSL bool `envconfig:"HANDLE_SSL" toml:"handle_ssl"` + // Whether to enable the builtin frontend + EnableUI bool `envconfig:"ENABLE_UI" toml:"enable_ui"` + Domain string `envconfig:"DOMAIN" toml:"domain"` } } @@ -66,7 +73,7 @@ func ReadConfig(cliArgs *CLIArguments) Config { if err != nil { log.Fatalf("Failed to parse config file %s with error %v\n", cliArgs.ConfigFile, err) } - + godotenv.Load() err = envconfig.Process("", &cfg) if err != nil { log.Fatalf("Failed to overwrite config from env. Error %v\n", err) diff --git a/endpoints/api/ap/ap.go b/endpoints/api/ap/ap.go new file mode 100644 index 0000000..d42615b --- /dev/null +++ b/endpoints/api/ap/ap.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 mStar +// +// Licensed under the EUPL, Version 1.2 +// +// You may not use this work except in compliance with the Licence. +// You should have received a copy of the Licence along with this work. If not, see: +// . +// See the Licence for the specific language governing permissions and limitations under the Licence. +// + +package ap + +import ( + "net/http" + + "github.com/julienschmidt/httprouter" + "gitlab.com/mstarongitlab/linstrom/server" +) + +func Register(server *server.Server) { + server.Router.GET("/api/ap/user/:id", GetUser(server)) +} + +func GetUser(server *server.Server) func(http.ResponseWriter, *http.Request, httprouter.Params) { + return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {} +} diff --git a/endpoints/api/ap/webfinger.go b/endpoints/api/ap/webfinger.go new file mode 100644 index 0000000..9c55734 --- /dev/null +++ b/endpoints/api/ap/webfinger.go @@ -0,0 +1,91 @@ +// Copyright (c) 2024 mStar +// +// Licensed under the EUPL, Version 1.2 +// +// You may not use this work except in compliance with the Licence. +// You should have received a copy of the Licence along with this work. If not, see: +// . +// See the Licence for the specific language governing permissions and limitations under the Licence. +// + +package ap + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/julienschmidt/httprouter" + "gitlab.com/mstarongitlab/goutils/other" + "gitlab.com/mstarongitlab/linstrom/server" + "gitlab.com/mstarongitlab/linstrom/types" +) + +var ErrInvalidResource = errors.New("invalid resource") + +type webfingerLink struct { + Rel string `json:"self"` + Type string `json:"type"` + Target url.URL `json:"href"` +} + +type WebfingerResponse struct { + Subject string `json:"subject"` + Links []webfingerLink `json:"links"` +} + +func WebfingerEndpoint(server *server.Server) func(http.ResponseWriter, *http.Request, httprouter.Params) { + return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + r.ParseForm() + req := r.FormValue("resource") + if req == "" { + http.NotFound(w, r) + return + } + acc, err := getAccountFromWebfingerResource(req) + if errors.Is(err, ErrInvalidResource) { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if acc.Host.Hostname() != server.Config.General.Domain { + http.Error(w, fmt.Sprintf("wrong host, try %s", &acc.Host), http.StatusNotFound) + return + } + + response := WebfingerResponse{ + Links: []webfingerLink{ + { + Rel: "me", + Type: "application/activity+json", + Target: *other.Must(url.Parse("https://example.com")), // TODO: Replace this with actual link generation + }, + }, + } + json.Marshal(response) + } +} + +// acct:bugle@bugle.lol + +func getAccountFromWebfingerResource(r string) (*types.AccountHandle, error) { + acctCheck, acc, found := strings.Cut(r, ":") + if !found || acctCheck != "acct" { + return nil, ErrInvalidResource + } + name, rawHost, found := strings.Cut(acc, "@") + if !found { + return nil, ErrInvalidResource + } + host, err := url.Parse(rawHost) + if err != nil { + return nil, fmt.Errorf("%w: Invalid url: %w", ErrInvalidResource, err) + } + + return &types.AccountHandle{ + Name: name, + Host: *host, + }, nil +} diff --git a/objects/person.go b/endpoints/endpoints.go similarity index 69% rename from objects/person.go rename to endpoints/endpoints.go index 305af92..0a42ab4 100644 --- a/objects/person.go +++ b/endpoints/endpoints.go @@ -8,7 +8,13 @@ // See the Licence for the specific language governing permissions and limitations under the Licence. // -package objects +package endpoints -type Person struct { +import ( + "gitlab.com/mstarongitlab/linstrom/endpoints/api/ap" + "gitlab.com/mstarongitlab/linstrom/server" +) + +func RegisterAll(s *server.Server) { + ap.Register(s) } diff --git a/go.mod b/go.mod index 2134fbc..040b534 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,10 @@ -module gitlab.com/beckersam/linstrom +module gitlab.com/mstarongitlab/linstrom go 1.21.5 require ( github.com/BurntSushi/toml v1.3.2 + github.com/julienschmidt/httprouter v1.3.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/sirupsen/logrus v1.9.3 gorm.io/driver/postgres v1.5.4 @@ -11,11 +12,15 @@ require ( ) require ( + github.com/MatejLach/astreams v0.6.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.4.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/lib/pq v1.10.9 // indirect + gitlab.com/mstarongitlab/goutils v0.0.0-20240117084827-7f9be06e1b58 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/go.sum b/go.sum index 61140f8..bd1f72a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/MatejLach/astreams v0.6.0 h1:vOrPTVc0ZdByMKSGAB3Sq52C6EFgSikr7phwc/64yxw= +github.com/MatejLach/astreams v0.6.0/go.mod h1:4Ji/fRmm9wAv0JzNWbLULOlpQ8YZ45s9rQjl8Vp85yw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -13,8 +15,14 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -24,6 +32,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gitlab.com/mstarongitlab/goutils v0.0.0-20240117084827-7f9be06e1b58 h1:mRislY6modrbqu9ck/3d+P1b/fqxEcuN/s67ULMgATY= +gitlab.com/mstarongitlab/goutils v0.0.0-20240117084827-7f9be06e1b58/go.mod h1:SvqfzFxgashuZPqR9kPwQ9gFA7I1yskZjhmGmY2pAow= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/main.go b/main.go index cdb818a..f3efef2 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,8 @@ package main import ( "fmt" - "gitlab.com/beckersam/linstrom/config" + "gitlab.com/mstarongitlab/linstrom/config" + "gitlab.com/mstarongitlab/linstrom/storage" ) func main() { @@ -21,4 +22,9 @@ func main() { fmt.Println(cliArgs) cfg := config.ReadConfig(&cliArgs) fmt.Println(cfg) + storage, err := storage.NewStorage(&cfg) + if err != nil { + panic(err) + } + fmt.Println(storage.GetFollowersFor("Placeholder")) } diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..510b57c --- /dev/null +++ b/server/server.go @@ -0,0 +1,13 @@ +package server + +import ( + "github.com/julienschmidt/httprouter" + "gitlab.com/mstarongitlab/linstrom/config" + "gitlab.com/mstarongitlab/linstrom/storage" +) + +type Server struct { + Router *httprouter.Router + Storage *storage.Storage + Config *config.Config +} diff --git a/storage/db_types.go b/storage/db_types.go deleted file mode 100644 index 5091c6d..0000000 --- a/storage/db_types.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2024 mStar -// -// Licensed under the EUPL, Version 1.2 -// -// You may not use this work except in compliance with the Licence. -// You should have received a copy of the Licence along with this work. If not, see: -// . -// See the Licence for the specific language governing permissions and limitations under the Licence. -// - -package storage - -import ( - "crypto/rsa" - "net/mail" - "net/url" - - "gorm.io/gorm" -) - -type personDB struct { - gorm.Model // Includes primary key (uid) as well as various time info - - // Public data - - uid string - name string - instance url.URL - local bool - publicKey string - - // Private data, set if local == true - - mail *mail.Address - pwHash *string - inbox url.URL - privateKey *rsa.PrivateKey - following []url.URL - followers []url.URL -} - -type noteDB struct { - gorm.Model // Includes primary key (uid) as well as various time info - - // Public data - - uid string - content string - creator url.URL - instance url.URL - local bool - tags []string - repliesTo *url.URL - deliverTo []url.URL - - // Private data - - // None yet -} diff --git a/objects/objects.go b/storage/follows.go similarity index 51% rename from objects/objects.go rename to storage/follows.go index 8fb1325..f1b6caa 100644 --- a/objects/objects.go +++ b/storage/follows.go @@ -8,4 +8,19 @@ // See the Licence for the specific language governing permissions and limitations under the Licence. // -package objects +package storage + +type Follow struct { + Follower Person + FollowerID uint + Follows Person + FollowsID uint +} + +func (s *Storage) GetFollowersFor(url string) ([]Follow, error) { + var followed *Person = &Person{} + s.Db.Table("people").First(&followed, "url = ?", url) + var f []Follow = make([]Follow, 0) + s.Db.Table("follows").Find(&f, "follows_id = ?", followed.ID) + return f, nil +} diff --git a/storage/note.go b/storage/note.go new file mode 100644 index 0000000..4c9269d --- /dev/null +++ b/storage/note.go @@ -0,0 +1,37 @@ +// Copyright (c) 2024 mStar +// +// Licensed under the EUPL, Version 1.2 +// +// You may not use this work except in compliance with the Licence. +// You should have received a copy of the Licence along with this work. If not, see: +// . +// See the Licence for the specific language governing permissions and limitations under the Licence. +// + +package storage + +import ( + "github.com/lib/pq" + "gorm.io/gorm" +) + +type Note struct { + gorm.Model // Includes primary key (uid) as well as various time info + + // Public data + + Uid string + Content string + CreatorID uint + Creator Person + Instance string // url + Local bool + Tags pq.StringArray `gorm:"type:text[]"` + RepliesTo *Note + RepliesToID *uint + DeliverTo pq.StringArray `gorm:"type:text[]"` // url + + // Private data + + // None yet +} diff --git a/storage/person.go b/storage/person.go new file mode 100644 index 0000000..19832ca --- /dev/null +++ b/storage/person.go @@ -0,0 +1,59 @@ +// Copyright (c) 2024 mStar +// +// Licensed under the EUPL, Version 1.2 +// +// You may not use this work except in compliance with the Licence. +// You should have received a copy of the Licence along with this work. If not, see: +// . +// See the Licence for the specific language governing permissions and limitations under the Licence. +// + +package storage + +import ( + "errors" + + "github.com/MatejLach/astreams" + "gorm.io/gorm" +) + +type Person struct { + gorm.Model // Includes primary key (uid) as well as various time info + + // Public data + + Uid string + ApID string + Name string + Instance string // url + Local bool + PublicKey string + Url string + ManuallyApprovesFollowers bool + + // Private data, set if local == true + + Mail *string // mail + PwHash *string + Inbox string // url + Outbox string // url + PrivateKey *string // private key +} + +func (pdb *Person) IntoAPPerson() (*astreams.Person, error) { + var actor astreams.Actor + actor.PublicKey = &astreams.PublicKey{ + ID: pdb.ApID, + Owner: pdb.Url, + PublicKeyPem: pdb.PublicKey, + } + actor.Inbox = &astreams.StringWithOrderedCollection{ + URL: pdb.Inbox, + } + actor.Outbox = &astreams.StringWithOrderedCollection{ + URL: pdb.Outbox, + } + actor.ManuallyApprovesFollowers = pdb.ManuallyApprovesFollowers + actor.PreferredUsername = pdb.Name + return nil, errors.New("unimplemented") +} diff --git a/storage/storage.go b/storage/storage.go index 0c6d783..a7cff87 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -13,16 +13,40 @@ package storage import ( "fmt" - "gitlab.com/beckersam/linstrom/config" + "gitlab.com/mstarongitlab/linstrom/config" "gorm.io/driver/postgres" "gorm.io/gorm" ) +type Storage struct { + Db *gorm.DB +} + func NewDb(cfg *config.Config) (*gorm.DB, error) { db, err := gorm.Open(postgres.Open(cfg.General.DbPath)) if err != nil { - return nil, fmt.Errorf("Failed to connect to db %s: %w", cfg.General.DbPath, err) + return nil, fmt.Errorf("failed to connect to db %s: %w", cfg.General.DbPath, err) + } + err = db.AutoMigrate( + &Person{}, + &Note{ + Tags: make([]string, 0), + DeliverTo: make([]string, 0), + }, + &Follow{}, + ) + if err != nil { + return nil, fmt.Errorf("failed to apply migrations: %w", err) } - db.AutoMigrate(&personDB{}, ¬eDB{}) return db, nil } + +func NewStorage(cfg *config.Config) (*Storage, error) { + db, err := NewDb(cfg) + if err != nil { + return nil, fmt.Errorf("failed to create database connection while creating storage: %w", err) + } + return &Storage{ + Db: db, + }, nil +} diff --git a/types/types.go b/types/types.go new file mode 100644 index 0000000..0f6462c --- /dev/null +++ b/types/types.go @@ -0,0 +1,8 @@ +package types + +import "net/url" + +type AccountHandle struct { + Name string + Host url.URL +}