From 12d54e4ff807620105a5605f3c4f2b074d474abc Mon Sep 17 00:00:00 2001 From: Wittano Bonarotti Date: Mon, 29 Jul 2024 23:16:14 +0200 Subject: [PATCH] feat: finished project --- Makefile | 43 +------ audio/file.go | 17 +++ audio/path.go | 147 +++++++++++++++++++++++ audio/path_test.go | 117 ++++++++++++++++++ cmd/server/main.go | 21 ---- cmd/tui/main.go | 9 -- db/migrations/000001_init.down.sql | 8 -- db/migrations/000001_init.up.sql | 103 ---------------- db/sql/jokes.sql | 33 ------ default.nix | 4 +- go.mod | 9 +- go.sum | 20 ---- go.work | 1 - go.work.sum | 2 + internal/audio/path_test.go | 2 +- internal/joke/types.go | 87 -------------- internal/mongodb/database.go | 20 ++-- internal/test/assets.go | 3 +- proto/admin.proto | 36 ------ proto/audio.proto | 50 -------- proto/joke.proto | 55 --------- proto/logs.proto | 37 ------ proto/options.proto | 31 ----- proto/request.proto | 37 ------ server/audio.go | 106 ----------------- server/audio_test.go | 183 ----------------------------- server/file.go | 80 ------------- server/file_test.go | 157 ------------------------- server/go.mod | 12 -- server/go.sum | 12 -- server/joke.go | 116 ------------------ server/pagination.go | 13 -- server/server.go | 48 -------- sqlc.yaml | 12 -- 34 files changed, 301 insertions(+), 1330 deletions(-) create mode 100644 audio/file.go create mode 100644 audio/path.go create mode 100644 audio/path_test.go delete mode 100644 cmd/server/main.go delete mode 100644 cmd/tui/main.go delete mode 100644 db/migrations/000001_init.down.sql delete mode 100644 db/migrations/000001_init.up.sql delete mode 100644 db/sql/jokes.sql delete mode 100644 proto/admin.proto delete mode 100644 proto/audio.proto delete mode 100644 proto/joke.proto delete mode 100644 proto/logs.proto delete mode 100644 proto/options.proto delete mode 100644 proto/request.proto delete mode 100644 server/audio.go delete mode 100644 server/audio_test.go delete mode 100644 server/file.go delete mode 100644 server/file_test.go delete mode 100644 server/go.mod delete mode 100644 server/go.sum delete mode 100644 server/joke.go delete mode 100644 server/pagination.go delete mode 100644 server/server.go delete mode 100644 sqlc.yaml diff --git a/Makefile b/Makefile index c6c8357..5cbab2c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ -DEST_DIR = /opt/komputer ARCH = $(shell uname -m) OUTPUT_DIR=./build PROTOBUF_API_DEST=./api @@ -11,50 +10,20 @@ endif .PHONY: test clean -bot-dev: proto gen-sql +bot-dev: CGO_ENABLED=1 GOOS=linux GOARCH=$(GOARCH) go build -tags dev -o $(OUTPUT_DIR)/komputer ./cmd/komputer/main.go -bot-prod: protobuf gen-sql +bot-prod: CGO_ENABLED=1 GOOS=linux GOARCH=$(GOARCH) go build -o $(OUTPUT_DIR)/komputer ./cmd/komputer/main.go -sever: protobuf gen-sql - go build -o $(OUTPUT_DIR)/server ./cmd/server/main.go +test: test-bot -tui: protobuf - go build -o $(OUTPUT_DIR)/tui ./cmd/tui/main.go - -protobuf: - mkdir -p $(PROTOBUF_API_DEST) - protoc --go_out=./api --go_opt=paths=source_relative --go-grpc_out=./api --go-grpc_opt=paths=source_relative proto/* - -test: test-bot test-server - -test-bot: protobuf gen-sql +test-bot: CGO_CFLAGS="-w" go test ./bot/...; -test-server: protobuf - CGO_CFLAGS="-w" go test ./server/...; - -all: bot-prod sever tui test - -DB_PATH=db.sqlite - -update-database: -ifeq (,$(wildcard $(DB_PATH))) - touch $(DB_PATH) -endif - migrate -database sqlite3://$(DB_PATH) -path db/migrations up - -gen-sql: - sqlc -f sqlc.yaml generate - -clean: cleanProto +all: bot-prod test +clean: ifneq ("$(wildcard $(OUTPUT_DIR))", "") rm -r $(OUTPUT_DIR) endif - -cleanProto: -ifneq ("$(wildcard $(PROTOBUF_API_DEST))", "") - rm -r $(PROTOBUF_API_DEST) -endif diff --git a/audio/file.go b/audio/file.go new file mode 100644 index 0000000..fcae236 --- /dev/null +++ b/audio/file.go @@ -0,0 +1,17 @@ +package audio + +import ( + "context" + "io" +) + +func Upload(ctx context.Context, r io.ReadCloser) (int, error) { + defer r.Close() + select { + case <-ctx.Done(): + return 0, context.Canceled + default: + } + + return 0, nil +} diff --git a/audio/path.go b/audio/path.go new file mode 100644 index 0000000..b6dfa89 --- /dev/null +++ b/audio/path.go @@ -0,0 +1,147 @@ +package audio + +import ( + "errors" + "fmt" + "io" + "math/rand" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" + "time" +) + +const ( + defaultCacheDirAudio = "assets" + assetsDirKey = "ASSETS_DIR" +) + +func Path(name string) (path string, err error) { + assertDir := AssertDir() + path = filepath.Join(assertDir, name) + _, err = os.Stat(path) + if err != nil { + path, err = searchPathByNameOrUUID(name) + } + + return +} + +func searchPathByNameOrUUID(prefix string) (p string, err error) { + var paths []string + paths, err = Paths() + if err != nil { + return + } + + for _, p = range paths { + base := filepath.Base(p) + if strings.HasPrefix(base, prefix) { + return + } else { + split := strings.Split(base, "-") + if len(split) < 2 { + continue + } + + if strings.HasPrefix(strings.Join(split[1:], "-"), prefix) { + return + } + } + } + + return "", fmt.Errorf("path with prefix %s wasn't found", prefix) +} + +func AssertDir() (path string) { + path = defaultCacheDirAudio + if cacheDir, ok := os.LookupEnv(assetsDirKey); ok && cacheDir != "" { + path = cacheDir + } + + return +} + +func Paths() (paths []string, err error) { + assertDir := AssertDir() + dirs, err := os.ReadDir(assertDir) + if err != nil { + return nil, err + } + + if len(dirs) <= 0 { + return nil, errors.New("assert directory is empty") + } + + paths = make([]string, 0, len(dirs)) + for _, dir := range dirs { + if dir.Type() != os.ModeDir { + paths = append(paths, filepath.Join(assertDir, dir.Name())) + } + } + + return +} + +// PathsWithPagination get fixed-sized list of audio paths from assert dictionary +func PathsWithPagination(page uint32, size uint32) (paths []string, err error) { + dirs, err := os.ReadDir(AssertDir()) + if err != nil { + return nil, err + } + + skipFiles := int(page * size) + if len(dirs) < skipFiles { + return []string{}, nil + } else if len(dirs) <= 0 { + return nil, errors.New("assert directory is empty") + } + + paths = make([]string, 0, size) + for _, dir := range dirs[skipFiles:] { + if dir.Type() != os.ModeDir { + paths = append(paths, dir.Name()) + } + + if len(paths) >= int(size) { + break + } + } + + return +} + +func RandomAudioName() (string, error) { + paths, err := Paths() + if err != nil { + return "", err + } + + return paths[rand.Int()%len(paths)], nil +} + +func Duration(path string) (duration time.Duration, err error) { + cmd := exec.Command("ffprobe", "-i", path, "-show_entries", "format=duration", "-v", "quiet", "-of", "csv='p=0'") + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } + + if err = cmd.Start(); err != nil { + return + } + + out, err := cmd.StdoutPipe() + if err != nil { + return + } + defer out.Close() + + rawTime, err := io.ReadAll(out) + if err != nil { + return + } + + return time.ParseDuration(string(rawTime) + "s") +} diff --git a/audio/path_test.go b/audio/path_test.go new file mode 100644 index 0000000..f9fe164 --- /dev/null +++ b/audio/path_test.go @@ -0,0 +1,117 @@ +package audio + +import ( + "errors" + "github.com/wittano/komputer/internal/test" + "os" + "path/filepath" + "strconv" + "strings" + "testing" +) + +func TestAudioIDs_AssetDirHasEmptyDirs(t *testing.T) { + dir := t.TempDir() + + for i := 0; i < 5; i++ { + os.Mkdir(filepath.Join(dir, strconv.Itoa(i)), 0700) + } + + if err := os.Setenv(assetsDirKey, dir); err != nil { + t.Fatal(err) + } + + paths, err := Paths() + if err != nil { + t.Fatal(err) + } + + if len(paths) != 0 { + t.Fatal("something was found in empty directory") + } +} + +func TestAudioIDs(t *testing.T) { + const expectedFilesNumber = 5 + if err := test.CreateAssertDir(t, expectedFilesNumber); err != nil { + t.Fatal(err) + } + + paths, err := Paths() + if err != nil { + t.Fatal(err) + } + + if len(paths) != expectedFilesNumber { + t.Fatalf("missing audios IDs. Expected '%d', Result: '%d'", expectedFilesNumber, len(paths)) + } + + for i, id := range paths { + fileID := filepath.Base(strings.Split(id, ".")[0]) + + if fileID != "test-"+strconv.Itoa(i) { + t.Fatalf("invalid ID. Expected: '%d', Result: '%s'", i, fileID) + } + } +} + +func TestPathsWithPagination_ButEmptyDictionary(t *testing.T) { + if err := os.Setenv(assetsDirKey, t.TempDir()); err != nil { + t.Fatal(err) + } + + res, err := PathsWithPagination(0, 10) + if err == nil { + t.Fatalf("Assert dictionary was found: %s", os.Getenv(assetsDirKey)) + } + + if len(res) != 0 { + t.Fatalf("Something was found in assert dictionary, but it doesn't expect. %v", res) + } +} + +func TestPathsWithPagination_PageIsOverANumberOfFiles(t *testing.T) { + const expectedFilesNumber = 5 + if err := test.CreateAssertDir(t, expectedFilesNumber); err != nil { + t.Fatal(err) + } + + res, err := PathsWithPagination(10, 10) + if err != nil { + t.Fatal(err) + } + + if len(res) != 0 { + t.Fatalf("Something was added to list, but page and size are over number of files. %v", res) + } +} + +func TestPathsWithPagination(t *testing.T) { + const expectedFilesNumber = 50 + if err := test.CreateAssertDir(t, expectedFilesNumber); err != nil { + t.Fatal(err) + } + + res, err := PathsWithPagination(2, 10) + if err != nil { + t.Fatal(err) + } + + if len(res) != 10 { + t.Fatalf("Files wasn't found but shoule be") + } + + const startFileSuffix = 20 + for i := startFileSuffix; i < 30; i++ { + f := res[i-startFileSuffix] + if _, err = os.Stat(f); !errors.Is(err, os.ErrNotExist) { + t.Fatal(err) + } + } +} + +func TestPathsWithPagination_AssertDirNotFound(t *testing.T) { + if _, err := PathsWithPagination(0, 10); err == nil { + t.Fatalf("Assert dictionary was found") + } +} diff --git a/cmd/server/main.go b/cmd/server/main.go deleted file mode 100644 index 582c9ad..0000000 --- a/cmd/server/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "flag" - "github.com/wittano/komputer/server" - "log" -) - -func main() { - port := flag.Uint64("port", 8080, "Server TCP port") - - s, err := server.New(*port) - if err != nil { - log.Fatalf("failed initialized server: %s", err) - } - defer s.Close() - - if err = s.Start(); err != nil { - log.Fatal(err) - } -} diff --git a/cmd/tui/main.go b/cmd/tui/main.go deleted file mode 100644 index 20f0d0f..0000000 --- a/cmd/tui/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "fmt" -) - -func main() { - fmt.Println("Hello World!") -} diff --git a/db/migrations/000001_init.down.sql b/db/migrations/000001_init.down.sql deleted file mode 100644 index d3b367b..0000000 --- a/db/migrations/000001_init.down.sql +++ /dev/null @@ -1,8 +0,0 @@ -drop table if exists jokes; -drop trigger if exists joke_add_event_trigger; -drop trigger if exists joke_update_event_trigger; -drop table if exists jokes_audit; -drop table if exists jokes_audit; -drop table if exists admins; -drop table if exists joke_types; -drop table if exists joke_categories; \ No newline at end of file diff --git a/db/migrations/000001_init.up.sql b/db/migrations/000001_init.up.sql deleted file mode 100644 index a1036fc..0000000 --- a/db/migrations/000001_init.up.sql +++ /dev/null @@ -1,103 +0,0 @@ --- create admins -create table admins -( - id integer primary key autoincrement, - name text unique not null -); - --- create default categories -create table joke_categories -( - id integer primary key autoincrement, - name text unique not null -); - -insert into joke_categories(name) -values ('Any'); -insert into joke_categories(name) -values ('Programming'); -insert into joke_categories(name) -values ('Misc'); -insert into joke_categories(name) -values ('Dark'); -insert into joke_categories(name) -values ('YoMama'); - --- create default types - -create table joke_types -( - id integer primary key autoincrement, - name text unique not null -); - -create trigger joke_types_lower_case_name - after insert - on joke_types -begin - update joke_types set name = lower(new.name) where id = new.id; -end; - -insert into joke_types(name) -values ('single'); -insert into joke_types(name) -values ('twopart'); - --- create jokes - -create table jokes -( - id integer primary key autoincrement, - question text, - answer text not null, - type_id integer not null references joke_types default 1, - category_id integer not null references joke_categories default 1, - userID text, - guildID text -); - -create index jokes_index on jokes (id, guildID); - -create table jokes_audit -( - id integer primary key autoincrement, - status text default 'CREATED', - joke_id integer not null, - question text, - answer text not null, - type_id integer not null references joke_types, - category_id integer not null references joke_categories, - userID text, - guildID text -); - -create trigger joke_add_event_trigger - after insert - on jokes -begin - insert - into jokes_audit(joke_id, question, answer, type_id, category_id, userID, guildID) - values (new.id, new.question, new.answer, new.type_id, new.category_id, new.userID, new.guildID); -end; - -create trigger joke_update_event_trigger - after update - on jokes -begin - insert - into jokes_audit(status, joke_id, question, answer, type_id, category_id, userID, guildID) - values ('UPDATED', new.id, new.question, new.answer, new.type_id, - new.category_id, - new.userID, new.guildID); -end; - -create trigger joke_delete_event_trigger - before delete - on jokes -begin - insert - into jokes_audit(status, joke_id, question, answer, type_id, category_id, userID, guildID) - values ('DELETE', old.id, old.question, old.answer, old.type_id, - old.category_id, - old.userID, old.guildID); -end; \ No newline at end of file diff --git a/db/sql/jokes.sql b/db/sql/jokes.sql deleted file mode 100644 index e6c10c6..0000000 --- a/db/sql/jokes.sql +++ /dev/null @@ -1,33 +0,0 @@ --- name: GetJokeById :one -select * -from jokes -where id == ? -limit 1; - --- name: GetTypes :many -select * -from joke_types; - --- name: GetCategories :many -select * -from joke_categories c; - --- name: AddJoke :exec -insert -into jokes(question, answer, type_id, category_id, userID, guildID) -values (?, ?, ?, ?, ?, ?); - --- name: RemoveJoke :exec -delete -from jokes -where id = ?; - --- name: UpdateJoke :exec -update jokes -set question = ?, - answer = ?, - type_id = ?, - category_id = ?, - userID = ?, - guildID = ? -where id = ?; \ No newline at end of file diff --git a/default.nix b/default.nix index a09f7ad..a315eb0 100644 --- a/default.nix +++ b/default.nix @@ -7,11 +7,11 @@ , opusfile }: buildGoModule { name = "komputer"; - version = "v1.2.0"; + version = "v1.2.1"; src = ./.; - vendorHash = "sha256-CThNuZ16b8SXxJAtCkDMm+mwCqaS5zrr+PbX+5N3GCc="; + vendorHash = "sha256-B/kII44/cuzGwO/pWCamFl7clHbz/qon4YgtUHYWV30="; CGO_ENABLED = 1; proxyVendor = true; diff --git a/go.mod b/go.mod index 34242ba..cf2c541 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,11 @@ go 1.22.2 toolchain go1.22.3 -replace ( - github.com/wittano/komputer/bot => ./bot - github.com/wittano/komputer/server => ./server -) +replace github.com/wittano/komputer/bot => ./bot require ( github.com/google/uuid v1.6.0 - github.com/jackc/pgx/v5 v5.6.0 github.com/wittano/komputer/bot v0.0.0-20240630190043-d1c8d2e0a118 - github.com/wittano/komputer/server v0.0.0 go.mongodb.org/mongo-driver v1.16.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 @@ -25,8 +20,6 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/websocket v1.5.3 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/montanaflynn/stats v0.7.1 // indirect diff --git a/go.sum b/go.sum index 5512e21..081dd4f 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ github.com/bwmarrin/dgvoice v0.0.0-20210225172318-caaac756e02e h1:IdfGDWLNL/ZAHd github.com/bwmarrin/dgvoice v0.0.0-20210225172318-caaac756e02e/go.mod h1:DT3heoMAQGrOExZ3Rb3TBOQ4Bm+wD4H48KFnt1YfLoQ= github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4= github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= -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= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -14,14 +13,6 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= -github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -30,13 +21,6 @@ github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2 github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -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= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -88,9 +72,5 @@ google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= layeh.com/gopus v0.0.0-20210501142526-1ee02d434e32 h1:/S1gOotFo2sADAIdSGk1sDq1VxetoCWr6f5nxOG0dpY= layeh.com/gopus v0.0.0-20210501142526-1ee02d434e32/go.mod h1:yDtyzWZDFCVnva8NGtg38eH2Ns4J0D/6hD+MMeUGdF0= diff --git a/go.work b/go.work index f76c315..ca60a9f 100644 --- a/go.work +++ b/go.work @@ -4,5 +4,4 @@ use ( . bot ./internal/dgvoice - server ) diff --git a/go.work.sum b/go.work.sum index 1b3de9c..bd32ef9 100644 --- a/go.work.sum +++ b/go.work.sum @@ -38,6 +38,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/lyft/protoc-gen-star/v2 v2.0.3 h1:/3+/2sWyXeMLzKd1bX+ixWKgEMsULrIivpDsuaF441o= diff --git a/internal/audio/path_test.go b/internal/audio/path_test.go index 088cb19..f9fe164 100644 --- a/internal/audio/path_test.go +++ b/internal/audio/path_test.go @@ -47,7 +47,7 @@ func TestAudioIDs(t *testing.T) { } for i, id := range paths { - fileID := strings.Split(id, ".")[0] + fileID := filepath.Base(strings.Split(id, ".")[0]) if fileID != "test-"+strconv.Itoa(i) { t.Fatalf("invalid ID. Expected: '%d', Result: '%s'", i, fileID) diff --git a/internal/joke/types.go b/internal/joke/types.go index 7234984..180727c 100644 --- a/internal/joke/types.go +++ b/internal/joke/types.go @@ -1,8 +1,6 @@ package joke import ( - "errors" - komputer "github.com/wittano/komputer/api/proto" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -24,70 +22,6 @@ const ( Any Category = "Any" ) -func (t Type) ApiType() (ty komputer.Type, err error) { - switch t { - case Single: - ty = komputer.Type_SINGLE - case TwoPart: - ty = komputer.Type_TWO_PART - default: - err = errors.New("joke: unknown type") - } - - return -} - -func (c Category) ApiCategory() (ca komputer.Category, err error) { - switch c { - case Any: - ca = komputer.Category_Any - case DARK: - ca = komputer.Category_DARK - case PROGRAMMING: - ca = komputer.Category_PROGRAMMING - case YOMAMA: - ca = komputer.Category_YOMAMA - case MISC: - ca = komputer.Category_MISC - default: - err = errors.New("joke: unknown category") - } - - return -} - -func RawType(api komputer.Type) (t Type, err error) { - switch api { - case komputer.Type_SINGLE: - t = Single - case komputer.Type_TWO_PART: - t = TwoPart - default: - err = errors.New("joke: unknown type") - } - - return -} - -func RawCategory(api komputer.Category) (c Category, err error) { - switch api { - case komputer.Category_Any: - c = Any - case komputer.Category_DARK: - c = DARK - case komputer.Category_PROGRAMMING: - c = PROGRAMMING - case komputer.Category_YOMAMA: - c = YOMAMA - case komputer.Category_MISC: - c = MISC - default: - err = errors.New("joke: unknown category") - } - - return -} - type DbModel struct { ID primitive.ObjectID `bson:"_id"` Question string `bson:"question"` @@ -96,24 +30,3 @@ type DbModel struct { Category Category `bson:"category"` GuildID string `bson:"guild_id"` } - -func (j DbModel) ApiResponse() (*komputer.Joke, error) { - ty, err := j.Type.ApiType() - if err != nil { - return nil, err - } - - ca, err := j.Category.ApiCategory() - if err != nil { - return nil, err - } - - return &komputer.Joke{ - Id: &komputer.ObjectID{ObjectId: j.ID.Hex()}, - Answer: j.Answer, - Question: &j.Question, - Type: ty, - Category: ca, - GuildId: j.GuildID, - }, nil -} diff --git a/internal/mongodb/database.go b/internal/mongodb/database.go index 40b6f29..50cdbc0 100644 --- a/internal/mongodb/database.go +++ b/internal/mongodb/database.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - komputer "github.com/wittano/komputer/api/proto" "github.com/wittano/komputer/internal" "github.com/wittano/komputer/internal/joke" "go.mongodb.org/mongo-driver/bson" @@ -64,7 +63,7 @@ func (d Service) Add(ctx context.Context, joke joke.DbModel) (string, error) { } func (d Service) RandomJoke(ctx context.Context, search internal.SearchParams) (joke.DbModel, error) { - jokes, err := d.Jokes(ctx, search, nil) + jokes, err := d.Jokes(ctx, search) if err != nil { return joke.DbModel{}, err } @@ -73,7 +72,7 @@ func (d Service) RandomJoke(ctx context.Context, search internal.SearchParams) ( } func (d Service) Joke(ctx context.Context, search internal.SearchParams) (joke.DbModel, error) { - jokes, err := d.Jokes(ctx, search, nil) + jokes, err := d.Jokes(ctx, search) if err != nil { return joke.DbModel{}, err } @@ -81,7 +80,11 @@ func (d Service) Joke(ctx context.Context, search internal.SearchParams) (joke.D return jokes[0], nil } -func (d Service) Jokes(ctx context.Context, search internal.SearchParams, page *komputer.Pagination) ([]joke.DbModel, error) { +func (d Service) Jokes(ctx context.Context, search internal.SearchParams) ([]joke.DbModel, error) { + // This function should accept pagination, but during creating gRPC server, + // I decided to revert all changes with gRPC API, because the project is finished. + // I don't add a new feature or extra tools to him + select { case <-ctx.Done(): return nil, context.Canceled @@ -113,15 +116,6 @@ func (d Service) Jokes(ctx context.Context, search internal.SearchParams, page * pageNr uint32 = 0 ) - if page != nil { - if page.Size > 0 { - pageSize = page.Size - } - if page.Page > 0 { - pageNr = page.Page - } - } - pipeline := mongo.Pipeline{{{ "$sample", bson.D{{ "size", pageSize, diff --git a/internal/test/assets.go b/internal/test/assets.go index 20629ff..43da857 100644 --- a/internal/test/assets.go +++ b/internal/test/assets.go @@ -2,7 +2,6 @@ package test import ( "fmt" - "github.com/google/uuid" "os" "path/filepath" "testing" @@ -19,7 +18,7 @@ func CreateAssertDir(t *testing.T, n int) (err error) { for i := 0; i < n; i++ { var f *os.File - f, err = os.Create(filepath.Join(dir, fmt.Sprintf("test-%s.mp3", uuid.NewString()))) + f, err = os.Create(filepath.Join(dir, fmt.Sprintf("test-%d.mp3", i))) if err != nil { return } diff --git a/proto/admin.proto b/proto/admin.proto deleted file mode 100644 index f8103dc..0000000 --- a/proto/admin.proto +++ /dev/null @@ -1,36 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/wittano/komputer"; -import "google/protobuf/empty.proto"; -import "proto/request.proto"; -package komputer; - -enum Privileges { - AUDIO = 0; - JOKE = 1; - FEATURE_MANAGEMENT = 2; -} - -message AdminRegistration { - string name = 1; - oneof auth_method { - string password = 2; - string public_key = 3; - } - string guildID = 4; - repeated Privileges privileges = 5; -} - -message AdminData { - bytes object_id = 1; - string name = 2; - string guildID = 3; - repeated Privileges privileges = 4; -} - -service AdminService { - rpc List(Pagination) returns (stream AdminData); - rpc New(AdminRegistration) returns (ObjectID); - rpc Update(AdminRegistration) returns (AdminData); - rpc Remove(ObjectID) returns (google.protobuf.Empty); -} \ No newline at end of file diff --git a/proto/audio.proto b/proto/audio.proto deleted file mode 100644 index a4cb1cc..0000000 --- a/proto/audio.proto +++ /dev/null @@ -1,50 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/wittano/komputer"; -import "google/protobuf/empty.proto"; -import "proto/request.proto"; -package komputer; - -enum FileFormat { - MP3 = 0; - MP4 = 1; -} - -message AudioInfo { - string name = 1; - FileFormat type = 2; -} - -message Audio { - AudioInfo info = 1; - bytes chunk = 2; -} - -message FileBuffer { - bytes content = 1; - uint64 size = 2; -} - -message UploadAudioResponse { - string filename = 1; - uint64 size = 2; -} - -message DownloadFile { - optional UUID uuid = 1; - optional string name = 2; -} - -message RemoveAudio { - repeated string name = 1; -} - -service AudioService { - rpc List(komputer.Pagination) returns (stream AudioInfo); - rpc Add(stream Audio) returns (UploadAudioResponse); - rpc Remove(RemoveAudio) returns (google.protobuf.Empty); -} - -service AudioFileService { - rpc Download(DownloadFile) returns (stream FileBuffer); -} \ No newline at end of file diff --git a/proto/joke.proto b/proto/joke.proto deleted file mode 100644 index 2053021..0000000 --- a/proto/joke.proto +++ /dev/null @@ -1,55 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/wittano/komputer"; -import "google/protobuf/empty.proto"; -import "proto/request.proto"; -package komputer; - -enum Type { - SINGLE = 0; - TWO_PART = 1; -} - -enum Category { - PROGRAMMING = 0; - MISC = 1; - DARK = 2; - YOMAMA = 3; - Any = 4; -} - -message Joke { - komputer.ObjectID id = 1; - string answer = 2; - optional string question = 3; - Type type = 4; - Category category = 5; - string guild_id = 6; -} - -message JokeID { - oneof id { - uint64 api_id = 1; - bytes object_id = 2; - } -} - -message JokeParams { - optional komputer.ObjectID id = 1; - optional Category category = 2; - optional Type type = 3; -} - -message JokeParamsPagination { - optional komputer.ObjectID id = 1; - optional Category category = 2; - optional Type type = 3; - Pagination page = 4; -} - -service JokeService { - rpc Find(JokeParams) returns (Joke); - rpc FindAll(JokeParamsPagination) returns (stream Joke); - rpc Add(Joke) returns (JokeID); - rpc Remove(komputer.ObjectID) returns (google.protobuf.Empty); -} \ No newline at end of file diff --git a/proto/logs.proto b/proto/logs.proto deleted file mode 100644 index 1ed2be9..0000000 --- a/proto/logs.proto +++ /dev/null @@ -1,37 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/wittano/komputer"; -import "google/protobuf/empty.proto"; -import "google/protobuf/timestamp.proto"; -package komputer; - -enum Level { - INFO = 0; - DEBUG = 1; - WARN = 2; - ERROR = 3; -} - -message Log { - bytes msg = 1; - optional Level level = 2; -} - -message LogDateRange { - google.protobuf.Timestamp from = 1; - google.protobuf.Timestamp to = 2; -} - -message LogData { - bytes data = 1; - uint32 size = 2; -} - -service StreamLogsService { - rpc Get(google.protobuf.Empty) returns (stream Log); -} - -service LogManager { - rpc CleanUp(LogDateRange) returns (google.protobuf.Empty); - rpc Download(LogDateRange) returns (stream LogData); -} \ No newline at end of file diff --git a/proto/options.proto b/proto/options.proto deleted file mode 100644 index 7a6f2e1..0000000 --- a/proto/options.proto +++ /dev/null @@ -1,31 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/wittano/komputer"; -package komputer; - -enum Features { - JOKE_DEV = 0; - HUMOR_API = 1; - DATABASE = 2; -} - -message DatabaseConfig { - string uri = 1; -} - -message HumorAPIConfig { - string api_key = 1; -} - -message FeatureStatus { - Features option = 1; - bool value = 2; - oneof config { - DatabaseConfig database = 3; - HumorAPIConfig humor_api = 4; - } -} - -service ConfigService { - rpc Update(FeatureStatus) returns (FeatureStatus); -} \ No newline at end of file diff --git a/proto/request.proto b/proto/request.proto deleted file mode 100644 index 97efbf0..0000000 --- a/proto/request.proto +++ /dev/null @@ -1,37 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/wittano/komputer"; -package komputer; - -message Pagination { - uint32 page = 1; - uint32 size = 2; -} - -message FindById { - oneof identity { - uint64 id = 1; - bytes uuid = 2; - } - optional Pagination page = 4; -} - -message UUID { - bytes uuid = 1; -} - -message ObjectID { - string object_id = 1; -} - -message FileQuery { - oneof query { - uint64 id = 1; - UUID uuid = 2; - string name = 3; - } -} - -message NameOrIdAudioRequest { - repeated FileQuery query = 1; -} \ No newline at end of file diff --git a/server/audio.go b/server/audio.go deleted file mode 100644 index 77ea9e9..0000000 --- a/server/audio.go +++ /dev/null @@ -1,106 +0,0 @@ -package server - -import ( - "context" - "errors" - "fmt" - "github.com/google/uuid" - pb "github.com/wittano/komputer/api/proto" - "github.com/wittano/komputer/internal/audio" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/emptypb" - "io" - "os" - "path/filepath" - "strings" -) - -type audioServer struct { - pb.UnimplementedAudioServiceServer -} - -func (a audioServer) List(pagination *pb.Pagination, server pb.AudioService_ListServer) (err error) { - page := paginationOrDefault(pagination) - - paths, err := audio.PathsWithPagination(page.Page, page.Size) - if err != nil { - return status.Error(codes.NotFound, err.Error()) - } - - for _, path := range paths { - err = errors.Join(err, server.Send(&pb.AudioInfo{Name: path, Type: pb.FileFormat_MP3})) - } - - return -} - -// TODO Add file validation -func (a audioServer) Add(server pb.AudioService_AddServer) error { - id := uuid.NewString() - - au, err := server.Recv() - if err != nil { - return status.Error(codes.Internal, err.Error()) - } - - if _, err := audio.Path(au.Info.Name); err == nil { - return status.Error(codes.AlreadyExists, fmt.Sprintf("file %s already exists", au.Info.Name)) - } - - path := filepath.Join(audio.AssertDir(), fmt.Sprintf("%s-%s.%s", au.Info.Name, id, strings.ToLower(au.Info.Type.String()))) - f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600) - if err != nil { - return status.Error(codes.FailedPrecondition, err.Error()) - } - defer f.Close() - - for { - select { - case <-server.Context().Done(): - return status.Error(codes.Canceled, context.Canceled.Error()) - default: - } - - if len(au.Chunk) == 0 { - break - } - - if _, err = f.Write(au.Chunk); err != nil { - return status.Error(codes.Internal, err.Error()) - } - - if au, err = server.Recv(); err != nil && errors.Is(err, io.EOF) { - break - } else if err != nil { - return status.Error(codes.Internal, err.Error()) - } - } - - stat, err := os.Stat(path) - if err != nil { - return status.Error(codes.NotFound, "uploaded file wasn't found") - } - - return server.SendAndClose(&pb.UploadAudioResponse{Size: uint64(stat.Size()), Filename: filepath.Base(path)}) -} - -func (a audioServer) Remove(_ context.Context, req *pb.RemoveAudio) (e *emptypb.Empty, err error) { - e = &emptypb.Empty{} - if req == nil { - return - } - - for _, query := range req.Name { - path, err := audio.Path(query) - if err != nil { - return nil, status.Error(codes.NotFound, err.Error()) - } - - if err = os.Remove(path); err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - } - - return -} diff --git a/server/audio_test.go b/server/audio_test.go deleted file mode 100644 index 9579c94..0000000 --- a/server/audio_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package server - -import ( - "bufio" - "context" - "errors" - pb "github.com/wittano/komputer/api/proto" - "github.com/wittano/komputer/internal/audio" - "github.com/wittano/komputer/internal/test" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "io" - "log" - "math/rand/v2" - "os" - "path/filepath" - "strconv" - "testing" - "time" -) - -const ( - port = 3000 -) - -type closers []io.Closer - -func (c closers) Close() (err error) { - for _, f := range c { - err = errors.Join(err, f.Close()) - } - - return -} - -func createAudioClient() (client pb.AudioServiceClient, server io.Closer, err error) { - s, err := New(port) - if err != nil { - return nil, nil, err - } - - go func() { - if err := s.Start(); err != nil { - log.Fatalf("Server exited with error: %v", err) - } - }() - - conn, err := grpc.NewClient("localhost:"+strconv.Itoa(port), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - s.Close() - return - } - - server = closers{conn, s} - client = pb.NewAudioServiceClient(conn) - return -} - -func TestRemoveAudio(t *testing.T) { - client, closer, err := createAudioClient() - if err != nil { - t.Fatal(err) - } - defer closer.Close() - - if err = test.CreateAssertDir(t, 5); err != nil { - t.Fatal(err) - } - - paths, err := audio.Paths() - if err != nil { - t.Fatal(err) - } - - for i, p := range paths { - paths[i] = filepath.Base(p) - } - - if _, err := client.Remove(context.Background(), &pb.RemoveAudio{Name: paths}); err != nil { - t.Fatal(err) - } -} - -func TestUploadFile(t *testing.T) { - client, closer, err := createAudioClient() - if err != nil { - t.Fatal(err) - } - defer closer.Close() - - if err = test.CreateAssertDir(t, 0); err != nil { - t.Fatal(err) - } - - path := t.TempDir() + "test.mp3" - if err := fillTempFile(t, path); err != nil { - t.Fatal(err) - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - stream, err := client.Add(ctx) - - f, err := os.Open(path) - if err != nil { - t.Fatal(err) - } - - filename := filepath.Base(path) - scan := bufio.NewScanner(f) - for scan.Scan() { - stream.Send(&pb.Audio{Info: &pb.AudioInfo{Name: filename, Type: pb.FileFormat_MP3}, Chunk: scan.Bytes()}) - } - - res, err := stream.CloseAndRecv() - if err != nil && !errors.Is(err, io.EOF) { - t.Fatal(err) - } - - path, err = audio.Path(res.Filename) - if err != nil { - t.Fatal(err) - } - - if s, err := os.Stat(path); err != nil || s.Size() == 0 { - t.Fatalf("failed upload file: %v", err) - } -} - -func TestUploadFile_FileAlreadyExists(t *testing.T) { - client, closer, err := createAudioClient() - if err != nil { - t.Fatal(err) - } - defer closer.Close() - - if err = test.CreateAssertDir(t, 0); err != nil { - t.Fatal(err) - } - - path := filepath.Join(audio.AssertDir(), "test") - f, createErr := os.Create(path) - if errors.Join(err, createErr) != nil { - t.Fatal(err) - } - f.Close() - - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - stream, err := client.Add(ctx) - - if err := stream.Send(&pb.Audio{Info: &pb.AudioInfo{Name: filepath.Base(path)}, Chunk: []byte{}}); err != nil { - t.Fatal(err) - } - - if _, err = stream.CloseAndRecv(); status.Code(err) != codes.AlreadyExists { - t.Fatalf("file %s shouldn't be uploaded", path) - } -} - -func fillTempFile(t *testing.T, path string) error { - if path == "" { - path = t.TempDir() + "test.mp3" - } - - f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600) - if err != nil { - return err - } - defer f.Close() - - for i := 0; i < 100; i++ { - if _, err := f.WriteString(strconv.Itoa(rand.Int()) + "\n"); err != nil { - return err - } - } - - return nil -} diff --git a/server/file.go b/server/file.go deleted file mode 100644 index abcff5e..0000000 --- a/server/file.go +++ /dev/null @@ -1,80 +0,0 @@ -package server - -import ( - "context" - "errors" - "fmt" - komputer "github.com/wittano/komputer/api/proto" - "github.com/wittano/komputer/internal/audio" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "io" - "log/slog" - "os" -) - -const downloadBufSize = 1024 * 1024 - -type fileServer struct { - komputer.UnimplementedAudioFileServiceServer -} - -func (fs fileServer) Download(req *komputer.DownloadFile, server komputer.AudioFileService_DownloadServer) error { - if req == nil { - return status.Error(codes.InvalidArgument, "download: missing required data") - } - - path, err := audio.Path(filename(req)) - if err != nil { - return status.Error(codes.NotFound, err.Error()) - } - - f, err := os.Open(path) - if err != nil { - slog.Error("failed find f "+path, err) - return status.Error(codes.NotFound, err.Error()) - } - defer f.Close() - - buf := make([]byte, downloadBufSize) - for { - select { - case <-server.Context().Done(): - return status.Error(codes.Canceled, context.Canceled.Error()) - default: - } - - n, err := f.Read(buf) - if errors.Is(err, io.EOF) { - break - } else if err != nil { - return status.Error(codes.Internal, err.Error()) - } - - if err = server.Send(&komputer.FileBuffer{Content: buf, Size: uint64(n)}); err != nil { - slog.Error("failed send chunk of file", err) - return status.Error(codes.Internal, err.Error()) - } - } - - return nil -} - -func filename(req *komputer.DownloadFile) (name string) { - if req == nil { - return - } - - var uuid *komputer.UUID - uuid, name = req.GetUuid(), req.GetName() - - if uuid == nil { - return - } - - if name == "" { - return string(uuid.Uuid) - } - - return fmt.Sprintf("%s-%s", name, uuid.Uuid) -} diff --git a/server/file_test.go b/server/file_test.go deleted file mode 100644 index f15a9c3..0000000 --- a/server/file_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package server - -import ( - "context" - "errors" - "fmt" - pb "github.com/wittano/komputer/api/proto" - "github.com/wittano/komputer/internal/audio" - "github.com/wittano/komputer/internal/test" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "io" - "log" - "os" - "path/filepath" - "strconv" - "strings" - "testing" - "time" -) - -func createDownloadClient() (client pb.AudioFileServiceClient, server io.Closer, err error) { - s, err := New(port) - if err != nil { - return nil, nil, err - } - - go func() { - if err := s.Start(); err != nil { - log.Fatalf("Server exited with error: %v", err) - } - }() - - conn, err := grpc.NewClient("localhost:"+strconv.Itoa(port), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - s.Close() - return - } - - server = closers{conn, s} - client = pb.NewAudioFileServiceClient(conn) - return -} - -func TestFileServer_Download_ButFileDoesNotExists(t *testing.T) { - client, closer, err := createDownloadClient() - if err != nil { - t.Fatal(err) - } - defer closer.Close() - - if err = test.CreateAssertDir(t, 1); err != nil { - t.Fatal(err) - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - name := "invalid_name" - stream, err := client.Download(ctx, &pb.DownloadFile{Name: &name}) - if err != nil { - t.Fatal(err) - } - defer stream.CloseSend() - - if _, err := stream.Recv(); status.Code(err) != codes.NotFound { - t.Fatal("file invalid_name wasn't found in assets dir. Status code: " + strconv.Itoa(int(status.Code(err)))) - } -} - -func TestFileServer_Download(t *testing.T) { - t.Parallel() - - client, closer, err := createDownloadClient() - if err != nil { - t.Fatal(err) - } - defer closer.Close() - - if err = test.CreateAssertDir(t, 1); err != nil { - t.Fatal(err) - } - - assertDir := audio.AssertDir() - dir, err := os.ReadDir(assertDir) - if err != nil { - t.Fatal(err) - } - - path := filepath.Join(assertDir, dir[0].Name()) - if err := fillTempFile(t, path); err != nil { - t.Fatal(err) - } - - base := filepath.Base(path) - split := strings.Split(base, "-") - if len(split) < 2 { - t.Fatal("invalid split " + base) - } - - uuid := pb.UUID{Uuid: []byte(strings.Join(split[1:], "-"))} - data := []*pb.DownloadFile{ - { - Name: &split[0], - }, - { - Uuid: &uuid, - }, - { - Name: &split[0], - Uuid: &uuid, - }, - } - - for _, d := range data { - var name string - if d.Name != nil { - name = *d.Name - } - - t.Run(fmt.Sprintf("download file with name: %s and uuid: %s", name, d.Uuid), func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - stream, err := client.Download(ctx, d) - if err != nil { - t.Fatal(err) - } - defer stream.CloseSend() - - for { - select { - case <-ctx.Done(): - return - default: - } - - recv, err := stream.Recv() - if err != nil && !errors.Is(err, io.EOF) { - t.Fatal(err) - } else if errors.Is(err, io.EOF) || (recv != nil && recv.Size < downloadBufSize) { - return - } - - if recv.Size == 0 { - t.Fatal("server didn't send chunk of file") - } - - if len(recv.Content) == 0 { - t.Fatalf("invalid size and number of bytes in content. Want: %d, Got: %d", recv.Size, len(recv.Content)) - } - } - }) - } -} diff --git a/server/go.mod b/server/go.mod deleted file mode 100644 index 3b6f547..0000000 --- a/server/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module github.com/wittano/komputer/server - -go 1.22 - -require ( - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/grpc v1.64.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect -) diff --git a/server/go.sum b/server/go.sum deleted file mode 100644 index b52b26e..0000000 --- a/server/go.sum +++ /dev/null @@ -1,12 +0,0 @@ -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/server/joke.go b/server/joke.go deleted file mode 100644 index 159dcc6..0000000 --- a/server/joke.go +++ /dev/null @@ -1,116 +0,0 @@ -package server - -import ( - "context" - "errors" - komputer "github.com/wittano/komputer/api/proto" - "github.com/wittano/komputer/internal" - "github.com/wittano/komputer/internal/joke" - "github.com/wittano/komputer/internal/mongodb" - "go.mongodb.org/mongo-driver/bson/primitive" - "google.golang.org/protobuf/types/known/emptypb" -) - -type jokeServer struct { - Db mongodb.Service - - komputer.UnimplementedJokeServiceServer -} - -type apiJokeParams interface { - GetId() *komputer.ObjectID - GetCategory() komputer.Category - GetType() komputer.Type -} - -func (j jokeServer) Find(ctx context.Context, params *komputer.JokeParams) (*komputer.Joke, error) { - p, err := searchParams(params) - if err != nil { - return nil, err - } - - entity, err := j.Db.Jokes(ctx, p, nil) - if err != nil { - return nil, err - } - - return entity[0].ApiResponse() -} - -func (j jokeServer) FindAll(identity *komputer.JokeParamsPagination, server komputer.JokeService_FindAllServer) error { - p, err := searchParams(identity) - if err != nil { - return err - } - - page := paginationOrDefault(identity.Page) - jokes, err := j.Db.Jokes(server.Context(), p, page) - if err != nil { - return err - } - - for _, jokeDb := range jokes { - response, err := jokeDb.ApiResponse() - if err != nil { - continue - } - - err = errors.Join(err, server.Send(response)) - } - - return err -} - -func (j jokeServer) Remove(ctx context.Context, id *komputer.ObjectID) (*emptypb.Empty, error) { - return &emptypb.Empty{}, j.Db.Remove(ctx, id.ObjectId) -} - -func (j jokeServer) Add(ctx context.Context, joke *komputer.Joke) (*komputer.JokeID, error) { - newJoke, err := newJoke(joke) - if err != nil { - return nil, err - } - - id, err := j.Db.Add(ctx, newJoke) - if err != nil { - return nil, err - } - - return &komputer.JokeID{Id: &komputer.JokeID_ObjectId{ObjectId: []byte(id)}}, err -} - -func newJoke(j *komputer.Joke) (new joke.DbModel, err error) { - if j == nil { - err = errors.New("missing joke data") - return - } - - new.Category, err = joke.RawCategory(j.Category) - new.Type, err = joke.RawType(j.Type) - new.Answer = j.Answer - new.GuildID = j.GuildId - new.ID = primitive.NewObjectID() - - if j.Question != nil { - new.Question = *j.Question - } - - return -} - -func searchParams(params apiJokeParams) (p internal.SearchParams, err error) { - if params == nil { - err = errors.New("missing joke params") - return - } - - id := params.GetId() - if id != nil { - p.ID, err = primitive.ObjectIDFromHex(id.ObjectId) - } - - p.Type, err = joke.RawType(params.GetType()) - p.Category, err = joke.RawCategory(params.GetCategory()) - - return -} diff --git a/server/pagination.go b/server/pagination.go deleted file mode 100644 index f813463..0000000 --- a/server/pagination.go +++ /dev/null @@ -1,13 +0,0 @@ -package server - -import komputer "github.com/wittano/komputer/api/proto" - -func paginationOrDefault(p *komputer.Pagination) *komputer.Pagination { - if p == nil { - return &komputer.Pagination{ - Size: 10, - } - } - - return p -} diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 1b2d44b..0000000 --- a/server/server.go +++ /dev/null @@ -1,48 +0,0 @@ -package server - -import ( - "context" - pb "github.com/wittano/komputer/api/proto" - "github.com/wittano/komputer/internal/mongodb" - "google.golang.org/grpc" - "log" - "net" - "strconv" -) - -type Server struct { - l net.Listener - cancelFunc context.CancelFunc - serv *grpc.Server -} - -func (s *Server) Start() error { - log.Printf("Server listing on port %serv\n", s.l.Addr()) - - return s.serv.Serve(s.l) -} - -func (s *Server) Close() error { - s.serv.Stop() - s.cancelFunc() - return s.l.Close() -} - -func New(port uint64) (*Server, error) { - l, err := net.Listen("tcp", "0.0.0.0:"+strconv.FormatUint(port, 10)) - if err != nil { - return nil, err - } - - s := grpc.NewServer() - ctx, cancel := context.WithCancel(context.Background()) - server := &Server{l, cancel, s} - - database := mongodb.NewMongodb(ctx) - - pb.RegisterJokeServiceServer(s, &jokeServer{Db: mongodb.Service{Db: database}}) - pb.RegisterAudioServiceServer(s, &audioServer{}) - pb.RegisterAudioFileServiceServer(s, &fileServer{}) - - return server, nil -} diff --git a/sqlc.yaml b/sqlc.yaml deleted file mode 100644 index 8c7a0fd..0000000 --- a/sqlc.yaml +++ /dev/null @@ -1,12 +0,0 @@ -version: "2" -sql: - - engine: "sqlite" - schema: "db/migrations" - queries: "db/sql" - database: - uri: sqlite://db.sqlite - gen: - go: - package: "sql" - out: "sql" - sql_package: "pgx/v5" \ No newline at end of file