From ab8df5f3da7eba8fadfaa7fdcbd0447b64a9464d Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Apr 2020 11:48:25 +0800 Subject: [PATCH 1/4] fix: add `Authorization` to CORS Allowed Headers --- backend/cors_middleware.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/cors_middleware.go b/backend/cors_middleware.go index f62eaa9..8f90187 100644 --- a/backend/cors_middleware.go +++ b/backend/cors_middleware.go @@ -10,7 +10,7 @@ func CorsMiddleware() gin.HandlerFunc { return cors.New(cors.Config{ //AllowOrigins: []string{"http://localhost:3000", "http://192.168.50.196:3000"}, AllowMethods: []string{"PUT", "PATCH", "POST", "GET", "OPTIONS"}, - AllowHeaders: []string{"Origin", "Content-Type"}, + AllowHeaders: []string{"Origin", "Content-Type", "Authorization"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, AllowAllOrigins: true, From e28df03cb45d33957928e42c08320de4d372a2ca Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Apr 2020 12:53:55 +0800 Subject: [PATCH 2/4] Update LICENSE --- LICENSE | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 201 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index bc934e7..25fc05e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -MIT License - -Copyright (c) 2020 Zed - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2020] [Zed] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From c5814ea3c1902cca17636bde1f77294b5349a5ab Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Apr 2020 23:26:11 +0800 Subject: [PATCH 3/4] test: add api test --- backend/routers/application_test.go | 105 ++++++++++++++++++++++++++++ backend/routers/main_test.go | 78 +++++++++++++++++++++ backend/routers/quotation_test.go | 71 +++++++++++++++++++ backend/routers/test_helper.go | 32 +++++++++ backend/routers/user_test.go | 64 +++++++++++++++++ backend/testdata/quotation.go | 45 ++++++++++++ backend/testdata/user.go | 30 ++++++++ 7 files changed, 425 insertions(+) create mode 100644 backend/routers/application_test.go create mode 100644 backend/routers/main_test.go create mode 100644 backend/routers/quotation_test.go create mode 100644 backend/routers/test_helper.go create mode 100644 backend/routers/user_test.go create mode 100644 backend/testdata/quotation.go create mode 100644 backend/testdata/user.go diff --git a/backend/routers/application_test.go b/backend/routers/application_test.go new file mode 100644 index 0000000..1079264 --- /dev/null +++ b/backend/routers/application_test.go @@ -0,0 +1,105 @@ +package routers + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "github.com/kastnerorz/animal-crossing-trading-system/backend/testdata" + "github.com/stretchr/testify/assert" + "net/http" + "testing" +) + +func TestCreateApplication(t *testing.T) { + t.Log("token", ApplicantToken) + t.Log("id", testdata.QuotationId) + body := []byte(`{"QuotationId":"` + testdata.QuotationId + `"}`) + r := PerformRequestWithAuth("POST", "/api/v1/applications", bytes.NewBuffer(body), ApplicantToken) + fmt.Println(r.Body) + assert.Equal(t, http.StatusCreated, r.Code) +} + +func TestGetMyApplicationsApplicant(t *testing.T) { + r := PerformRequestWithAuth("GET", "/api/v1/applications?type=APPLY", nil, ApplicantToken) + assert.Equal(t, http.StatusOK, r.Code) + + var applications []models.Application + err := json.Unmarshal([]byte(r.Body.String()), &applications) + application := applications[0] + ApplicationId = application.ID + assert.Nil(t, err) + assert.Equal(t, testdata.QuotationId, application.QuotationId.Hex()) + assert.Equal(t, "FRIENDS", application.QuotationType) + assert.Equal(t, "", application.PassCode) + assert.Equal(t, "", application.SwitchFriendCode) + assert.Equal(t, "PENDING", application.Status) +} + +func TestUpdateApplication(t *testing.T) { + body := []byte(`{"status":"ACCEPT","switchFriendCode":"SW-1234-1234-1234"}`) + r := PerformRequestWithAuth("PUT", "/api/v1/applications/"+ApplicationId, bytes.NewBuffer(body), ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) +} + +func TestGetMyApplicationsReviewer(t *testing.T) { + r := PerformRequestWithAuth("GET", "/api/v1/applications?type=REVIEW", nil, ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) + + var applications []models.Application + err := json.Unmarshal([]byte(r.Body.String()), &applications) + application := applications[0] + ApplicationId = application.ID + assert.Nil(t, err) + assert.Equal(t, testdata.QuotationId, application.QuotationId.Hex()) + assert.Equal(t, "FRIENDS", application.QuotationType) + assert.Equal(t, "", application.PassCode) + assert.Equal(t, "SW-1234-1234-1234", application.SwitchFriendCode) + assert.Equal(t, "ACCEPT", application.Status) +} + +// type PASS_CODE + +func TestCreateApplicationPassCode(t *testing.T) { + body := []byte(`{"QuotationId":"` + testdata.QuotationIdPassCode + `"}`) + r := PerformRequestWithAuth("POST", "/api/v1/applications", bytes.NewBuffer(body), ApplicantToken) + assert.Equal(t, http.StatusCreated, r.Code) +} + +func TestGetMyApplicationsApplicantPassCode(t *testing.T) { + r := PerformRequestWithAuth("GET", "/api/v1/applications?type=APPLY", nil, ApplicantToken) + assert.Equal(t, http.StatusOK, r.Code) + + var applications []models.Application + err := json.Unmarshal([]byte(r.Body.String()), &applications) + application := applications[0] + ApplicationId = application.ID + assert.Nil(t, err) + assert.Equal(t, testdata.QuotationIdPassCode, application.QuotationId.Hex()) + assert.Equal(t, "PASS_CODE", application.QuotationType) + assert.Equal(t, "", application.PassCode) + assert.Equal(t, "", application.SwitchFriendCode) + assert.Equal(t, "PENDING", application.Status) +} + +func TestUpdateApplicationPassCode(t *testing.T) { + body := []byte(`{"status":"ACCEPT","passCode":"56HMS"}`) + r := PerformRequestWithAuth("PUT", "/api/v1/applications/"+ApplicationId, bytes.NewBuffer(body), ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) +} + +func TestGetMyApplicationsReviewerPassCode(t *testing.T) { + r := PerformRequestWithAuth("GET", "/api/v1/applications?type=REVIEW", nil, ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) + + var applications []models.Application + err := json.Unmarshal([]byte(r.Body.String()), &applications) + application := applications[0] + ApplicationId = application.ID + assert.Nil(t, err) + assert.Equal(t, testdata.QuotationIdPassCode, application.QuotationId.Hex()) + assert.Equal(t, "PASS_CODE", application.QuotationType) + assert.Equal(t, "56HMS", application.PassCode) + assert.Equal(t, "", application.SwitchFriendCode) + assert.Equal(t, "ACCEPT", application.Status) +} diff --git a/backend/routers/main_test.go b/backend/routers/main_test.go new file mode 100644 index 0000000..3c05450 --- /dev/null +++ b/backend/routers/main_test.go @@ -0,0 +1,78 @@ +package routers + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "github.com/kastnerorz/animal-crossing-trading-system/backend/configs" + "github.com/kastnerorz/animal-crossing-trading-system/backend/pkg" + "github.com/kastnerorz/animal-crossing-trading-system/backend/testdata" + "go.mongodb.org/mongo-driver/bson" + "os" + "testing" +) + +func TestMain(m *testing.M) { + flag.StringVar(&configs.MongoURI, "mongo-url", "mongodb://localhost:27017", "") + flag.StringVar(&configs.MongoCollection, "mongo-collection", "acts-test1", "") + flag.StringVar(&configs.Port, "port", "8080", "") + flag.Parse() + + //inputOptions := mongoimport.InputOptions{ + // File: "./testdata/users.json", + //} + //opt := mongoimport.Options{ + // ToolOptions: nil, + // InputOptions: &inputOptions, + // ParsedArgs: nil, + //} + //mi, _ := mongoimport.New(opt) + //mi.SessionProvider, _ = db.NewSessionProvider(options.ToolOptions{ + // URI: &options.URI{ + // ConnectionString: "mongodb://localhost:27017/acts-test", + // ConnString: connstring.ConnString{}, + // }, + //}) + //success, failure, err := mi.ImportDocuments() + //fmt.Println(success, failure, err) + + //load data + mongoCtx, collection := pkg.GetMongoContext("users") + _, err := collection.InsertMany(mongoCtx, testdata.TestUsers()) + if err != nil { + fmt.Println(err) + } + + mongoCtx, collection = pkg.GetMongoContext("quotations") + _, err = collection.InsertMany(mongoCtx, testdata.TestQuotations()) + if err != nil { + fmt.Println(err) + } + + // login + body := []byte(`{"username":"zed","password":"01db71ab8048f74a4b92c26ba77285ade0687ac192758e8185ad52701f649ef2"}`) + r := PerformRequest("POST", "/api/v1/login", bytes.NewBuffer(body)) + var res map[string]string + json.Unmarshal([]byte(r.Body.String()), &res) + ReviewerToken, _ = res["token"] + + body = []byte(`{"username":"zed1","password":"01db71ab8048f74a4b92c26ba77285ade0687ac192758e8185ad52701f649ef2"}`) + r = PerformRequest("POST", "/api/v1/login", bytes.NewBuffer(body)) + json.Unmarshal([]byte(r.Body.String()), &res) + ApplicantToken, _ = res["token"] + + testResult := m.Run() + + //clean database + mongoCtx, collection = pkg.GetMongoContext("users") + collection.DeleteMany(mongoCtx, bson.M{}) + + mongoCtx, collection = pkg.GetMongoContext("quotations") + collection.DeleteMany(mongoCtx, bson.M{}) + + mongoCtx, collection = pkg.GetMongoContext("applications") + collection.DeleteMany(mongoCtx, bson.M{}) + + os.Exit(testResult) +} diff --git a/backend/routers/quotation_test.go b/backend/routers/quotation_test.go new file mode 100644 index 0000000..795faf1 --- /dev/null +++ b/backend/routers/quotation_test.go @@ -0,0 +1,71 @@ +package routers + +import ( + "bytes" + "encoding/json" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "github.com/stretchr/testify/assert" + "net/http" + "testing" +) + +var quotationId string + +func TestCreateQuotation(t *testing.T) { + body := []byte(`{"type":"SELL","price":40,"openType":"PASS_CODE","passCode":"56HMS","handlingFee":100000}`) + r := PerformRequestWithAuth("POST", "/api/v1/quotations", bytes.NewBuffer(body), ReviewerToken) + assert.Equal(t, http.StatusCreated, r.Code) +} + +func TestGetQuotations(t *testing.T) { + r := PerformRequest("GET", "/api/v1/quotations?type=SELL", nil) + assert.Equal(t, http.StatusOK, r.Code) + + var quotations []models.Quotation + err := json.Unmarshal([]byte(r.Body.String()), "ations) + quotation := quotations[0] + quotationId = quotation.ID + assert.Nil(t, err) + assert.Equal(t, 40, quotation.Price) + assert.Equal(t, "PASS_CODE", quotation.OpenType) + assert.Equal(t, "", quotation.PassCode) + assert.Equal(t, 100000, quotation.HandlingFee) +} + +func TestUpdateQuotation(t *testing.T) { + body := []byte(`{"price":90,"handlingFee":0,"openType":"FRIENDS"}`) + r := PerformRequestWithAuth("PUT", "/api/v1/quotations/"+quotationId, bytes.NewBuffer(body), ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) +} + +func TestGetMyQuotation(t *testing.T) { + r := PerformRequestWithAuth("GET", "/api/v1/quotations/my", nil, ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) + + var quotations []models.Quotation + err := json.Unmarshal([]byte(r.Body.String()), "ations) + assert.Nil(t, err) + quotation := quotations[0] + assert.Equal(t, 90, quotation.Price) + assert.Equal(t, "FRIENDS", quotation.OpenType) + assert.Equal(t, 0, quotation.HandlingFee) +} + +func TestCreateQuotationPassCode(t *testing.T) { + body := []byte(`{"type":"SELL","price":40,"openType":"PASS_CODE","passCode":"56HMS","handlingFee":100000}`) + r := PerformRequestWithAuth("POST", "/api/v1/quotations", bytes.NewBuffer(body), ReviewerToken) + assert.Equal(t, http.StatusCreated, r.Code) +} + +func TestGetMyQuotationPassCode(t *testing.T) { + r := PerformRequestWithAuth("GET", "/api/v1/quotations/my", nil, ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) + + var quotations []models.Quotation + err := json.Unmarshal([]byte(r.Body.String()), "ations) + quotation := quotations[0] + assert.Nil(t, err) + assert.Equal(t, 40, quotation.Price) + assert.Equal(t, "PASS_CODE", quotation.OpenType) + assert.Equal(t, 100000, quotation.HandlingFee) +} diff --git a/backend/routers/test_helper.go b/backend/routers/test_helper.go new file mode 100644 index 0000000..4aea8fd --- /dev/null +++ b/backend/routers/test_helper.go @@ -0,0 +1,32 @@ +package routers + +import ( + "io" + "net/http" + "net/http/httptest" +) + +var router = SetupRouter() + +func PerformRequest(method, url string, body io.Reader) *httptest.ResponseRecorder { + req, _ := http.NewRequest(method, url, body) + req.Header.Set("Content-Type", "application/json") + r := httptest.NewRecorder() + router.ServeHTTP(r, req) + return r +} + +func PerformRequestWithAuth(method, url string, body io.Reader, token string) *httptest.ResponseRecorder { + req, _ := http.NewRequest(method, url, body) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + r := httptest.NewRecorder() + router.ServeHTTP(r, req) + return r +} + +var QuotationId string +var QuotationIdPassCode string +var ApplicationId string +var ReviewerToken string +var ApplicantToken string diff --git a/backend/routers/user_test.go b/backend/routers/user_test.go new file mode 100644 index 0000000..d2e26ae --- /dev/null +++ b/backend/routers/user_test.go @@ -0,0 +1,64 @@ +package routers + +import ( + "bytes" + "encoding/json" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "github.com/stretchr/testify/assert" + "net/http" + "testing" +) + +var userId string + +func TestCreateUser(t *testing.T) { + // normal + body := []byte(`{"username":"zed2","nickname":"\u5f20\u8c46","password":"01db71ab8048f74a4b92c26ba77285ade0687ac192758e8185ad52701f649ef2","switchFriendCode":"SW-1234-1234-1234","jikeId":"\u5f20\u8c46"}`) + r := PerformRequest("POST", "/api/v1/users", bytes.NewBuffer(body)) + assert.Equal(t, http.StatusCreated, r.Code) + + var res map[string]string + err := json.Unmarshal([]byte(r.Body.String()), &res) + userId, _ = res["id"] + t.Log("UserId: " + userId) + assert.Nil(t, err) + + // 400 username is already exist + body = []byte(`{"username":"zed","nickname":"\u5f20\u8c46","password":"01db71ab8048f74a4b92c26ba77285ade0687ac192758e8185ad52701f649ef2","switchFriendCode":"SW-1234-1234-1234","jikeId":"\u5f20\u8c46"}`) + r = PerformRequest("POST", "/api/v1/users", bytes.NewBuffer(body)) + assert.Equal(t, http.StatusBadRequest, r.Code) + +} + +func TestGetUser(t *testing.T) { + + t.Log("UserId: " + userId) + // normal + r := PerformRequest("GET", "/api/v1/users/"+userId, nil) + assert.Equal(t, http.StatusOK, r.Code) + + var user models.User + err := json.Unmarshal([]byte(r.Body.String()), &user) + assert.Nil(t, err) + assert.Equal(t, userId, user.ID) + assert.Equal(t, "zed2", user.Username) + assert.Equal(t, "张豆", user.Nickname) + + // 404 + r = PerformRequest("GET", "/api/v1/users/r"+userId, nil) + assert.Equal(t, http.StatusNotFound, r.Code) +} + +func TestGetMyInfo(t *testing.T) { + // normal + r := PerformRequestWithAuth("GET", "/api/v1/me", nil, ReviewerToken) + assert.Equal(t, http.StatusOK, r.Code) + + var user models.User + err := json.Unmarshal([]byte(r.Body.String()), &user) + assert.Nil(t, err) + assert.Equal(t, "zed", user.Username) + assert.Equal(t, "张豆", user.Nickname) + assert.Equal(t, "SW-1234-1234-1234", user.SwitchFriendCode) + assert.Equal(t, "张豆", user.JikeID) +} diff --git a/backend/testdata/quotation.go b/backend/testdata/quotation.go new file mode 100644 index 0000000..e767750 --- /dev/null +++ b/backend/testdata/quotation.go @@ -0,0 +1,45 @@ +package testdata + +import ( + "github.com/kastnerorz/animal-crossing-trading-system/backend/tools" + "go.mongodb.org/mongo-driver/bson" +) + +var QuotationId = "5e8359a0f2f2d8c751c92b39" +var QuotationIdPassCode = "5e8300cd6dab4e49d227ca6b" + +func TestQuotations() []interface{} { + return []interface{}{ + bson.M{ + "_id": tools.ObjectID(QuotationIdPassCode), + "type": "SELL", + "price": 40, + "author": bson.M{ + "_id": "5e82ffb52fc4557c9d343b46", + "nickname": "张豆", + "jikeId": "张豆", + }, + "validCount": 0, + "invalidCount": 0, + "openType": "PASS_CODE", + "handlingFee": 100000, + "lastModified": "2020-03-31T08:35:25.963Z", + }, + bson.M{ + "_id": tools.ObjectID(QuotationId), + "type": "SELL", + "price": 40, + "author": bson.M{ + "_id": "5e82ffb52fc4557c9d343b46", + "nickname": "张豆", + "jikeId": "张豆", + }, + "validCount": 0, + "invalidCount": 0, + "openType": "FRIENDS", + "handlingFee": 100000, + "lastModified": "2020-03-31T14:54:24.143Z", + }, + } + +} diff --git a/backend/testdata/user.go b/backend/testdata/user.go new file mode 100644 index 0000000..6ee0436 --- /dev/null +++ b/backend/testdata/user.go @@ -0,0 +1,30 @@ +package testdata + +import ( + "github.com/kastnerorz/animal-crossing-trading-system/backend/tools" + "go.mongodb.org/mongo-driver/bson" +) + +var ReviewerID = "5e82ffb52fc4557c9d343b46" +var ApplicantId = "5e82ffb52fc4557c9d343b47" + +func TestUsers() []interface{} { + return []interface{}{ + bson.M{ + "_id": tools.ObjectID(ReviewerID), + "username": "zed", + "password": "b5609be1d47cca6abca4004253321f530fc4ff022e11683e9c36fc77769b24ae", + "nickname": "张豆", + "switchFriendCode": "SW-1234-1234-1234", + "jikeId": "张豆", + }, + bson.M{ + "_id": tools.ObjectID(ApplicantId), + "username": "zed1", + "password": "b5609be1d47cca6abca4004253321f530fc4ff022e11683e9c36fc77769b24ae", + "nickname": "张豆", + "switchFriendCode": "SW-1234-1234-1234", + "jikeId": "张豆", + }, + } +} From 312df88a03a00324bd11d74f05f8d5b71eba8738 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 1 Apr 2020 23:27:30 +0800 Subject: [PATCH 4/4] refactor: re-arrange project structure according to golang-standards/project-layout --- backend/{ => configs}/config.go | 2 +- backend/go.mod | 6 +++ backend/go.sum | 18 +++++++ backend/main.go | 17 ++++++ .../cors.go} | 2 +- .../jwt.go} | 16 +++--- backend/{ => models}/types.go | 2 +- backend/{ => pkg}/mongo.go | 7 +-- .../application.go} | 43 ++++++++------- .../quotation.go} | 54 ++++++++++--------- backend/{server.go => routers/router.go} | 17 +++--- .../{user_controller.go => routers/user.go} | 25 +++++---- backend/{ => tools}/helper.go | 16 ++++-- 13 files changed, 141 insertions(+), 84 deletions(-) rename backend/{ => configs}/config.go (80%) create mode 100644 backend/main.go rename backend/{cors_middleware.go => middlewares/cors.go} (96%) rename backend/{auth_middleware.go => middlewares/jwt.go} (88%) rename backend/{ => models}/types.go (99%) rename backend/{ => pkg}/mongo.go (78%) rename backend/{application_controller.go => routers/application.go} (80%) rename backend/{quotation_controller.go => routers/quotation.go} (77%) rename backend/{server.go => routers/router.go} (65%) rename backend/{user_controller.go => routers/user.go} (78%) rename backend/{ => tools}/helper.go (58%) diff --git a/backend/config.go b/backend/configs/config.go similarity index 80% rename from backend/config.go rename to backend/configs/config.go index 4df9598..ea09c85 100644 --- a/backend/config.go +++ b/backend/configs/config.go @@ -1,4 +1,4 @@ -package main +package configs var Port string diff --git a/backend/go.mod b/backend/go.mod index 23dfe72..eb8e054 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -6,5 +6,11 @@ require ( github.com/appleboy/gin-jwt/v2 v2.6.3 github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.6.2 + github.com/jessevdk/go-flags v1.4.0 // indirect + github.com/mongodb/mongo-tools v0.0.0-20200330084541-9c745274c318 + github.com/mongodb/mongo-tools-common v3.0.2+incompatible + github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/stretchr/testify v1.4.0 go.mongodb.org/mongo-driver v1.3.1 + gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index eecdbe6..6529861 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -62,12 +62,18 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -97,6 +103,10 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mongodb/mongo-tools v0.0.0-20200330084541-9c745274c318 h1:K+VIpZr5hb2yzVkR2eviNMjkVmKQbbmGGUQ7SqJw15Q= +github.com/mongodb/mongo-tools v0.0.0-20200330084541-9c745274c318/go.mod h1:hkFunXA3Gbksiojtxr/hjqPMckbYrey/EwEqjaHG2h8= +github.com/mongodb/mongo-tools-common v3.0.2+incompatible h1:+ToUdanjfLOttOuryXpN0I+D6gvO8IcAIqUJcJVlQq0= +github.com/mongodb/mongo-tools-common v3.0.2+incompatible/go.mod h1:T0AGIWI5PvxSzqt5zsR5zlcHl52KGeAzhoAnSvvpEtU= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -110,6 +120,10 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -142,6 +156,7 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkpre golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -163,6 +178,7 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -174,6 +190,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs= +gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/backend/main.go b/backend/main.go new file mode 100644 index 0000000..a6f2662 --- /dev/null +++ b/backend/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "flag" + "github.com/kastnerorz/animal-crossing-trading-system/backend/configs" + "github.com/kastnerorz/animal-crossing-trading-system/backend/routers" +) + +func main() { + flag.StringVar(&configs.MongoURI, "mongo-url", "mongodb://localhost:27017", "") + flag.StringVar(&configs.MongoCollection, "mongo-collection", "acts-dev", "") + flag.StringVar(&configs.Port, "port", "8080", "") + flag.Parse() + + router := routers.SetupRouter() + router.Run(":" + configs.Port) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") +} diff --git a/backend/cors_middleware.go b/backend/middlewares/cors.go similarity index 96% rename from backend/cors_middleware.go rename to backend/middlewares/cors.go index 8f90187..01ae1c0 100644 --- a/backend/cors_middleware.go +++ b/backend/middlewares/cors.go @@ -1,4 +1,4 @@ -package main +package middlewares import ( "github.com/gin-contrib/cors" diff --git a/backend/auth_middleware.go b/backend/middlewares/jwt.go similarity index 88% rename from backend/auth_middleware.go rename to backend/middlewares/jwt.go index b585f78..baeee69 100644 --- a/backend/auth_middleware.go +++ b/backend/middlewares/jwt.go @@ -1,10 +1,12 @@ -package main +package middlewares import ( "crypto/sha256" "encoding/hex" "github.com/appleboy/gin-jwt/v2" "github.com/gin-gonic/gin" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "github.com/kastnerorz/animal-crossing-trading-system/backend/pkg" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "log" @@ -23,7 +25,7 @@ func AuthMiddleware() *jwt.GinJWTMiddleware { MaxRefresh: time.Hour, IdentityKey: IdentityKey, PayloadFunc: func(data interface{}) jwt.MapClaims { - if v, ok := data.(*User); ok { + if v, ok := data.(*models.User); ok { return jwt.MapClaims{ IdentityKey: v.Username, } @@ -32,8 +34,8 @@ func AuthMiddleware() *jwt.GinJWTMiddleware { }, IdentityHandler: func(c *gin.Context) interface{} { claims := jwt.ExtractClaims(c) - mongoCtx, collection := GetMongoContext("users") - var user User + mongoCtx, collection := pkg.GetMongoContext("users") + var user models.User err := collection.FindOne(mongoCtx, bson.M{"username": claims[IdentityKey].(string)}).Decode(&user) if err != nil && err != mongo.ErrNoDocuments { @@ -45,15 +47,15 @@ func AuthMiddleware() *jwt.GinJWTMiddleware { return &user }, Authenticator: func(c *gin.Context) (interface{}, error) { - var credentials Credentials + var credentials models.Credentials if err := c.BindJSON(&credentials); err != nil { return "", jwt.ErrMissingLoginValues } h := sha256.New() h.Write([]byte(credentials.Password)) passwordHash := hex.EncodeToString(h.Sum(nil)) - mongoCtx, collection := GetMongoContext("users") - var res User + mongoCtx, collection := pkg.GetMongoContext("users") + var res models.User err := collection.FindOne(mongoCtx, bson.M{"username": credentials.Username}).Decode(&res) if err != nil && err != mongo.ErrNoDocuments { diff --git a/backend/types.go b/backend/models/types.go similarity index 99% rename from backend/types.go rename to backend/models/types.go index 1b4d94f..c3287bd 100644 --- a/backend/types.go +++ b/backend/models/types.go @@ -1,4 +1,4 @@ -package main +package models import ( "go.mongodb.org/mongo-driver/bson/primitive" diff --git a/backend/mongo.go b/backend/pkg/mongo.go similarity index 78% rename from backend/mongo.go rename to backend/pkg/mongo.go index d202ac9..491b1cc 100644 --- a/backend/mongo.go +++ b/backend/pkg/mongo.go @@ -1,7 +1,8 @@ -package main +package pkg import ( "context" + "github.com/kastnerorz/animal-crossing-trading-system/backend/configs" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "log" @@ -12,7 +13,7 @@ func InitiateMongoClient() *mongo.Client { var err error var client *mongo.Client opts := options.Client() - opts.ApplyURI(MongoURI) + opts.ApplyURI(configs.MongoURI) opts.SetMaxPoolSize(5) if client, err = mongo.Connect(context.Background(), opts); err != nil { log.Println(err.Error()) @@ -22,7 +23,7 @@ func InitiateMongoClient() *mongo.Client { func GetMongoContext(collection string) (context.Context, *mongo.Collection) { client := InitiateMongoClient() - db := client.Database(MongoCollection) + db := client.Database(configs.MongoCollection) col := db.Collection(collection) ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) return ctx, col diff --git a/backend/application_controller.go b/backend/routers/application.go similarity index 80% rename from backend/application_controller.go rename to backend/routers/application.go index ad73b3f..fbdf79a 100644 --- a/backend/application_controller.go +++ b/backend/routers/application.go @@ -1,8 +1,11 @@ -package main +package routers import ( "fmt" "github.com/gin-gonic/gin" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "github.com/kastnerorz/animal-crossing-trading-system/backend/pkg" + "github.com/kastnerorz/animal-crossing-trading-system/backend/tools" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" @@ -13,9 +16,9 @@ import ( ) func CreateApplication(c *gin.Context) { - user := GetUserFromContext(c) + user := tools.GetUserFromContext(c) - var applicationParam ApplicationParam + var applicationParam models.ApplicationParam err := c.BindJSON(&applicationParam) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": -1, "msg": "(-1)内部错误!"}) @@ -29,8 +32,8 @@ func CreateApplication(c *gin.Context) { return } - mongoCtx, collection := GetMongoContext("quotations") - var quotation Quotation + mongoCtx, collection := pkg.GetMongoContext("quotations") + var quotation models.Quotation objectId, _ := primitive.ObjectIDFromHex(applicationParam.QuotationId) err = collection.FindOne(mongoCtx, bson.M{"_id": objectId}).Decode("ation) if err != nil && err != mongo.ErrNoDocuments { @@ -45,7 +48,7 @@ func CreateApplication(c *gin.Context) { return } - mongoCtx, collection = GetMongoContext("applications") + mongoCtx, collection = pkg.GetMongoContext("applications") user.Password = "" user.Username = "" quotationId, _ := primitive.ObjectIDFromHex(quotation.ID) @@ -70,28 +73,28 @@ func CreateApplication(c *gin.Context) { } func GetMyApplications(c *gin.Context) { - user := GetUserFromContext(c) + user := tools.GetUserFromContext(c) filter := bson.M{} applicationType := c.Query("type") if applicationType != "" { - if _, ok := ApplicationType[applicationType]; !ok { + if _, ok := models.ApplicationType[applicationType]; !ok { c.JSON(http.StatusBadRequest, gin.H{"code": -1, "msg": "类型不合法"}) return } if applicationType == "REVIEW" { filter["reviewerId"], _ = primitive.ObjectIDFromHex(user.ID) } else if applicationType == "APPLY" { - filter["applicant"] = user.ID + filter["applicant._id"] = user.ID } } - lowerBound, upperBound := GetValidDateLowerAndUpperBound() + lowerBound, upperBound := tools.GetValidDateLowerAndUpperBound() filter["lastModified"] = bson.M{ "$gt": lowerBound, "$lte": upperBound, } - mongoCtx, collection := GetMongoContext("applications") + mongoCtx, collection := pkg.GetMongoContext("applications") opts := options.Find() opts.SetSort(bson.D{{"lastModified", -1}}) cursor, err := collection.Find(mongoCtx, filter, opts) @@ -100,7 +103,7 @@ func GetMyApplications(c *gin.Context) { log.Println(err) return } - var res []Application + var res []models.Application if err = cursor.All(mongoCtx, &res); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": -2, "msg": "申请查询失败!"}) log.Println(err) @@ -108,16 +111,16 @@ func GetMyApplications(c *gin.Context) { } cursor.Close(mongoCtx) if res == nil { - res = []Application{} + res = []models.Application{} } c.JSON(http.StatusOK, res) } func UpdateApplication(c *gin.Context) { - user := GetUserFromContext(c) + user := tools.GetUserFromContext(c) - var applicationParam ApplicationParam + var applicationParam models.ApplicationParam err := c.BindJSON(&applicationParam) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": -1, "msg": "(-1)内部错误!"}) @@ -125,8 +128,8 @@ func UpdateApplication(c *gin.Context) { return } - mongoCtx, collection := GetMongoContext("applications") - var application Application + mongoCtx, collection := pkg.GetMongoContext("applications") + var application models.Application objectId, _ := primitive.ObjectIDFromHex(c.Param("id")) err = collection.FindOne(mongoCtx, bson.M{"_id": objectId}).Decode(&application) if err != nil && err != mongo.ErrNoDocuments { @@ -147,7 +150,7 @@ func UpdateApplication(c *gin.Context) { } if applicationParam.Status == "" { - if _, ok := ApplicationStatus[applicationParam.Status]; !ok || applicationParam.Status == "PENDING" { + if _, ok := models.ApplicationStatus[applicationParam.Status]; !ok || applicationParam.Status == "PENDING" { c.JSON(http.StatusBadRequest, gin.H{"code": -2, "msg": "申请结果不合法!"}) log.Println(err) return @@ -163,7 +166,7 @@ func UpdateApplication(c *gin.Context) { return } set["passCode"] = applicationParam.PassCode - mongoCtx, collection = GetMongoContext("quotations") + mongoCtx, collection = pkg.GetMongoContext("quotations") _, err = collection.UpdateOne(mongoCtx, bson.M{"_id": application.QuotationId}, bson.M{"$set": bson.M{ "passCode": applicationParam.PassCode, }}) @@ -183,7 +186,7 @@ func UpdateApplication(c *gin.Context) { } set["status"] = applicationParam.Status - mongoCtx, collection = GetMongoContext("applications") + mongoCtx, collection = pkg.GetMongoContext("applications") _, err = collection.UpdateOne(mongoCtx, bson.M{"_id": objectId}, bson.M{"$set": set}) if err != nil && err != mongo.ErrNoDocuments { c.JSON(http.StatusInternalServerError, gin.H{"code": -1, "msg": "(-1)内部错误"}) diff --git a/backend/quotation_controller.go b/backend/routers/quotation.go similarity index 77% rename from backend/quotation_controller.go rename to backend/routers/quotation.go index fe35476..3b1c7ea 100644 --- a/backend/quotation_controller.go +++ b/backend/routers/quotation.go @@ -1,7 +1,11 @@ -package main +package routers import ( "github.com/gin-gonic/gin" + "github.com/kastnerorz/animal-crossing-trading-system/backend/middlewares" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "github.com/kastnerorz/animal-crossing-trading-system/backend/pkg" + "github.com/kastnerorz/animal-crossing-trading-system/backend/tools" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" @@ -12,10 +16,10 @@ import ( ) func CreateQuotation(c *gin.Context) { - o, _ := c.Get(IdentityKey) - user := o.(*User) + o, _ := c.Get(middlewares.IdentityKey) + user := o.(*models.User) - var quotation Quotation + var quotation models.Quotation err := c.BindJSON("ation) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": -1, "msg": "(-1)内部错误!"}) @@ -23,12 +27,12 @@ func CreateQuotation(c *gin.Context) { return } - if _, ok := QuotationType[quotation.Type]; !ok { + if _, ok := models.QuotationType[quotation.Type]; !ok { c.JSON(http.StatusBadRequest, gin.H{"code": -2, "msg": "报价类型不正确!"}) return } - if _, ok := OpenType[quotation.OpenType]; !ok { + if _, ok := models.OpenType[quotation.OpenType]; !ok { c.JSON(http.StatusBadRequest, gin.H{"code": -2, "msg": "岛屿开放类型不正确!"}) return } @@ -38,7 +42,7 @@ func CreateQuotation(c *gin.Context) { return } - mongoCtx, collection := GetMongoContext("quotations") + mongoCtx, collection := pkg.GetMongoContext("quotations") user.Password = "" user.SwitchFriendCode = "" user.Username = "" @@ -69,7 +73,7 @@ func GetQuotations(c *gin.Context) { filter := bson.M{} if quotationType != "" { - if _, ok := QuotationType[quotationType]; !ok { + if _, ok := models.QuotationType[quotationType]; !ok { c.JSON(http.StatusOK, []struct{}{}) return } @@ -78,7 +82,7 @@ func GetQuotations(c *gin.Context) { } if openType != "" { - if _, ok := OpenType[openType]; !ok { + if _, ok := models.OpenType[openType]; !ok { c.JSON(http.StatusOK, []struct{}{}) return } @@ -94,12 +98,12 @@ func GetQuotations(c *gin.Context) { // } //} - lowerBound, upperBound := GetValidDateLowerAndUpperBound() + lowerBound, upperBound := tools.GetValidDateLowerAndUpperBound() filter["lastModified"] = bson.M{ "$gt": lowerBound, "$lte": upperBound, } - mongoCtx, collection := GetMongoContext("quotations") + mongoCtx, collection := pkg.GetMongoContext("quotations") opts := options.Find() opts.SetSort(bson.D{{"price", -1}}) opts.SetLimit(10) @@ -112,7 +116,7 @@ func GetQuotations(c *gin.Context) { log.Println(err) return } - var res []Quotation + var res []models.Quotation if err = cursor.All(mongoCtx, &res); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": -2, "msg": "报价查询失败!"}) log.Println(err) @@ -120,26 +124,26 @@ func GetQuotations(c *gin.Context) { } cursor.Close(mongoCtx) if res == nil { - res = []Quotation{} + res = []models.Quotation{} } c.JSON(http.StatusOK, res) return } func GetMyQuotation(c *gin.Context) { - o, _ := c.Get(IdentityKey) - userId := o.(*User).ID + o, _ := c.Get(middlewares.IdentityKey) + userId := o.(*models.User).ID filter := bson.M{ "author._id": userId, } - lowerBound, upperBound := GetValidDateLowerAndUpperBound() + lowerBound, upperBound := tools.GetValidDateLowerAndUpperBound() filter["lastModified"] = bson.M{ "$gt": lowerBound, "$lte": upperBound, } - mongoCtx, collection := GetMongoContext("quotations") + mongoCtx, collection := pkg.GetMongoContext("quotations") opts := options.Find() opts.SetSort(bson.D{{"lastModified", -1}}) opts.SetLimit(1) @@ -149,7 +153,7 @@ func GetMyQuotation(c *gin.Context) { log.Println(err) return } - var res []Quotation + var res []models.Quotation if err = cursor.All(mongoCtx, &res); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": -2, "msg": "报价查询失败!"}) log.Println(err) @@ -157,17 +161,17 @@ func GetMyQuotation(c *gin.Context) { } cursor.Close(mongoCtx) if res == nil { - res = []Quotation{} + res = []models.Quotation{} } c.JSON(http.StatusOK, res) return } func UpdateQuotation(c *gin.Context) { - o, _ := c.Get(IdentityKey) - user := o.(*User) + o, _ := c.Get(middlewares.IdentityKey) + user := o.(*models.User) - var param QuotationParam + var param models.QuotationParam err := c.BindJSON(¶m) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": -1, "msg": "内部错误!"}) @@ -186,7 +190,7 @@ func UpdateQuotation(c *gin.Context) { } if openType != "" { - if _, ok := OpenType[openType]; !ok { + if _, ok := models.OpenType[openType]; !ok { c.JSON(http.StatusBadRequest, gin.H{"code": -2, "msg": "岛屿开放类型不正确!"}) return } @@ -201,8 +205,8 @@ func UpdateQuotation(c *gin.Context) { set["handlingFee"] = handlingFee } - var quotation Quotation - mongoCtx, collection := GetMongoContext("quotations") + var quotation models.Quotation + mongoCtx, collection := pkg.GetMongoContext("quotations") objectId, _ := primitive.ObjectIDFromHex(c.Param("id")) opt := options.FindOneAndUpdate() opt.SetReturnDocument(options.After) diff --git a/backend/server.go b/backend/routers/router.go similarity index 65% rename from backend/server.go rename to backend/routers/router.go index 3e771b9..cc15a87 100644 --- a/backend/server.go +++ b/backend/routers/router.go @@ -1,19 +1,14 @@ -package main +package routers import ( - "flag" "github.com/gin-gonic/gin" + "github.com/kastnerorz/animal-crossing-trading-system/backend/middlewares" ) -func main() { - flag.StringVar(&MongoURI, "mongo-url", "mongodb://localhost:27017", "") - flag.StringVar(&MongoCollection, "mongo-collection", "acts-dev", "") - flag.StringVar(&Port, "port", "8080", "") - flag.Parse() - authMiddleware := AuthMiddleware() - +func SetupRouter() *gin.Engine { router := gin.Default() - router.Use(CorsMiddleware()) + router.Use(middlewares.CorsMiddleware()) + authMiddleware := middlewares.AuthMiddleware() v1 := router.Group("/api/v1") { @@ -37,5 +32,5 @@ func main() { } router.Use(gin.Recovery()) - router.Run(":" + Port) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") + return router } diff --git a/backend/user_controller.go b/backend/routers/user.go similarity index 78% rename from backend/user_controller.go rename to backend/routers/user.go index df0aa5f..22f3fb3 100644 --- a/backend/user_controller.go +++ b/backend/routers/user.go @@ -1,9 +1,12 @@ -package main +package routers import ( "crypto/sha256" "encoding/hex" "github.com/gin-gonic/gin" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "github.com/kastnerorz/animal-crossing-trading-system/backend/pkg" + "github.com/kastnerorz/animal-crossing-trading-system/backend/tools" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" @@ -14,7 +17,7 @@ import ( func CreateUser(c *gin.Context) { - var user User + var user models.User err := c.BindJSON(&user) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": -1, "msg": "(-1)内部错误!"}) @@ -27,8 +30,8 @@ func CreateUser(c *gin.Context) { return } - mongoCtx, collection := GetMongoContext("users") - var res User + mongoCtx, collection := pkg.GetMongoContext("users") + var res models.User err = collection.FindOne(mongoCtx, bson.M{"username": user.Username}).Decode(&res) if err != nil && err != mongo.ErrNoDocuments { c.JSON(http.StatusInternalServerError, gin.H{"code": -3, "msg": "(-3)内部错误!"}) @@ -60,8 +63,8 @@ func CreateUser(c *gin.Context) { h := sha256.New() h.Write([]byte(user.Password)) - mongoCtx, collection = GetMongoContext("users") - _, err = collection.InsertOne(mongoCtx, bson.M{ + mongoCtx, collection = pkg.GetMongoContext("users") + insertResult, err := collection.InsertOne(mongoCtx, bson.M{ "username": user.Username, "password": hex.EncodeToString(h.Sum(nil)), "nickname": user.Nickname, @@ -73,13 +76,13 @@ func CreateUser(c *gin.Context) { log.Println(err) return } - - c.JSON(http.StatusCreated, gin.H{"username": user.Username}) + id := insertResult.InsertedID.(primitive.ObjectID).Hex() + c.JSON(http.StatusCreated, gin.H{"username": user.Username, "id": id}) } func GetUser(c *gin.Context) { - mongoCtx, collection := GetMongoContext("users") - var res User + mongoCtx, collection := pkg.GetMongoContext("users") + var res models.User objectId, _ := primitive.ObjectIDFromHex(c.Param("id")) opt := options.FindOne() opt.SetProjection(bson.D{ @@ -102,7 +105,7 @@ func GetUser(c *gin.Context) { } func GetMyInfo(c *gin.Context) { - user := GetUserFromContext(c) + user := tools.GetUserFromContext(c) user.Password = "" c.JSON(http.StatusOK, user) } diff --git a/backend/helper.go b/backend/tools/helper.go similarity index 58% rename from backend/helper.go rename to backend/tools/helper.go index 5de3e6e..d9bb8e4 100644 --- a/backend/helper.go +++ b/backend/tools/helper.go @@ -1,7 +1,10 @@ -package main +package tools import ( "github.com/gin-gonic/gin" + "github.com/kastnerorz/animal-crossing-trading-system/backend/middlewares" + "github.com/kastnerorz/animal-crossing-trading-system/backend/models" + "go.mongodb.org/mongo-driver/bson/primitive" "time" ) @@ -19,8 +22,13 @@ func GetValidDateLowerAndUpperBound() (time.Time, time.Time) { return lowerBound, upperBound } -func GetUserFromContext(c *gin.Context) *User { - o, _ := c.Get(IdentityKey) - user := o.(*User) +func GetUserFromContext(c *gin.Context) *models.User { + o, _ := c.Get(middlewares.IdentityKey) + user := o.(*models.User) return user } + +func ObjectID(id string) primitive.ObjectID { + oid, _ := primitive.ObjectIDFromHex(id) + return oid +}