├── .env.example
├── .gitignore
├── README.md
├── controllers
├── authController.go
├── categoryController.go
├── currencyController.go
├── productController.go
├── storeController.go
└── userController.go
├── database
├── database.go
└── seeder
│ └── seeder.go
├── front
├── .env.example
├── .eslintrc.json
├── .gitignore
├── README.md
├── components
│ ├── cart.tsx
│ └── layout
│ │ ├── base.tsx
│ │ └── nav.tsx
├── next-env.d.ts
├── next.config.js
├── package.json
├── pages
│ ├── _app.tsx
│ ├── index.tsx
│ ├── product
│ │ └── [id].tsx
│ ├── shop.tsx
│ ├── signin.tsx
│ └── signup.tsx
├── postcss.config.js
├── public
│ ├── favicon.ico
│ └── vercel.svg
├── server.js
├── store
│ ├── cart.ts
│ ├── products.ts
│ ├── store.ts
│ ├── toggleCart.tsx
│ └── userAuth.ts
├── styles
│ ├── Home.module.css
│ └── globals.css
├── tailwind.config.js
├── tsconfig.json
└── util
│ ├── axios.ts
│ └── storage.ts
├── go.mod
├── go.sum
├── main.go
├── middlewares
└── auth.go
├── models
├── cart.go
├── cartitems.go
├── categories.go
├── coupons.go
├── currency.go
├── order.go
├── product.go
├── product_sold.go
├── reviews.go
├── store.go
├── user.go
└── user_types.go
├── routes
└── routes.go
├── runner.conf
└── util
└── helper.go
/.env.example:
--------------------------------------------------------------------------------
1 | PORT=3000
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/go,vs,nextjs,react,dotenv
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,vs,nextjs,react,dotenv
4 |
5 | ### dotenv ###
6 | .env
7 |
8 | ### pnpm ###
9 | front/pnpm-lock.yaml
10 |
11 | ### Intellij ###
12 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
13 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
14 | /**/.idea/*
15 | # User-specific stuff:
16 | .idea/workspace.xml
17 | .idea/tasks.xml
18 | .idea/dictionaries
19 | .idea/vcs.xml
20 | .idea/jsLibraryMappings.xml
21 |
22 | # Sensitive or high-churn files:
23 | .idea/dataSources.ids
24 | .idea/dataSources.xml
25 | .idea/dataSources.local.xml
26 | .idea/sqlDataSources.xml
27 | .idea/dynamic.xml
28 | .idea/uiDesigner.xml
29 |
30 | # Gradle:
31 | .idea/gradle.xml
32 | .idea/libraries
33 |
34 | # Mongo Explorer plugin:
35 | .idea/mongoSettings.xml
36 |
37 | ## File-based project format:
38 | *.iws
39 |
40 | ## Plugin-specific files:
41 |
42 | # IntelliJ
43 | /out/
44 |
45 | # mpeltonen/sbt-idea plugin
46 | .idea_modules/
47 |
48 | # JIRA plugin
49 | atlassian-ide-plugin.xml
50 |
51 | # Crashlytics plugin (for Android Studio and IntelliJ)
52 | com_crashlytics_export_strings.xml
53 | crashlytics.properties
54 | crashlytics-build.properties
55 | fabric.properties
56 |
57 | ### Intellij Patch ###
58 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
59 |
60 | # *.iml
61 | # modules.xml
62 |
63 | ### Go ###
64 | # If you prefer the allow list template instead of the deny list, see community template:
65 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
66 | #
67 | # Binaries for programs and plugins
68 | *.exe
69 | *.exe~
70 | *.dll
71 | *.so
72 | *.dylib
73 |
74 | # Test binary, built with `go test -c`
75 | *.test
76 |
77 | # Output of the go coverage tool, specifically when used with LiteIDE
78 | *.out
79 |
80 | # Dependency directories (remove the comment below to include it)
81 | # vendor/
82 |
83 | # Go workspace file
84 | go.work
85 |
86 | ### Go Patch ###
87 | /vendor/
88 | /Godeps/
89 |
90 | ### NextJS ###
91 | # Next build dir
92 | .next/
93 |
94 | ### react ###
95 | .DS_*
96 | *.log
97 | logs
98 | **/*.backup.*
99 | **/*.back.*
100 |
101 | node_modules
102 | bower_components
103 |
104 | *.sublime*
105 |
106 | psd
107 | thumb
108 | sketch
109 |
110 | ### vs ###
111 | ## Ignore Visual Studio temporary files, build results, and
112 | ## files generated by popular Visual Studio add-ons.
113 | ##
114 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
115 |
116 | # User-specific files
117 | *.rsuser
118 | *.suo
119 | *.user
120 | *.userosscache
121 | *.sln.docstates
122 |
123 | # User-specific files (MonoDevelop/Xamarin Studio)
124 | *.userprefs
125 |
126 | # Mono auto generated files
127 | mono_crash.*
128 |
129 | # Build results
130 | [Dd]ebug/
131 | [Dd]ebugPublic/
132 | [Rr]elease/
133 | [Rr]eleases/
134 | x64/
135 | x86/
136 | [Aa][Rr][Mm]/
137 | [Aa][Rr][Mm]64/
138 | bld/
139 | [Bb]in/
140 | [Oo]bj/
141 | [Ll]og/
142 | [Ll]ogs/
143 |
144 | # Visual Studio 2015/2017 cache/options directory
145 | .vs/
146 | # Uncomment if you have tasks that create the project's static files in wwwroot
147 | #wwwroot/
148 |
149 | # Visual Studio 2017 auto generated files
150 | Generated\ Files/
151 |
152 | # MSTest test Results
153 | [Tt]est[Rr]esult*/
154 | [Bb]uild[Ll]og.*
155 |
156 | # NUnit
157 | *.VisualState.xml
158 | TestResult.xml
159 | nunit-*.xml
160 |
161 | # Build Results of an ATL Project
162 | [Dd]ebugPS/
163 | [Rr]eleasePS/
164 | dlldata.c
165 |
166 | # Benchmark Results
167 | BenchmarkDotNet.Artifacts/
168 |
169 | # .NET Core
170 | project.lock.json
171 | project.fragment.lock.json
172 | artifacts/
173 |
174 | # StyleCop
175 | StyleCopReport.xml
176 |
177 | # Files built by Visual Studio
178 | *_i.c
179 | *_p.c
180 | *_h.h
181 | *.ilk
182 | *.meta
183 | *.obj
184 | *.iobj
185 | *.pch
186 | *.pdb
187 | *.ipdb
188 | *.pgc
189 | *.pgd
190 | *.rsp
191 | *.sbr
192 | *.tlb
193 | *.tli
194 | *.tlh
195 | *.tmp
196 | *.tmp_proj
197 | *_wpftmp.csproj
198 | *.vspscc
199 | *.vssscc
200 | .builds
201 | *.pidb
202 | *.svclog
203 | *.scc
204 |
205 | # Chutzpah Test files
206 | _Chutzpah*
207 |
208 | # Visual C++ cache files
209 | ipch/
210 | *.aps
211 | *.ncb
212 | *.opendb
213 | *.opensdf
214 | *.sdf
215 | *.cachefile
216 | *.VC.db
217 | *.VC.VC.opendb
218 |
219 | # Visual Studio profiler
220 | *.psess
221 | *.vsp
222 | *.vspx
223 | *.sap
224 |
225 | # Visual Studio Trace Files
226 | *.e2e
227 |
228 | # TFS 2012 Local Workspace
229 | $tf/
230 |
231 | # Guidance Automation Toolkit
232 | *.gpState
233 |
234 | # ReSharper is a .NET coding add-in
235 | _ReSharper*/
236 | *.[Rr]e[Ss]harper
237 | *.DotSettings.user
238 |
239 | # TeamCity is a build add-in
240 | _TeamCity*
241 |
242 | # DotCover is a Code Coverage Tool
243 | *.dotCover
244 |
245 | # AxoCover is a Code Coverage Tool
246 | .axoCover/*
247 | !.axoCover/settings.json
248 |
249 | # Coverlet is a free, cross platform Code Coverage Tool
250 | coverage*[.json, .xml, .info]
251 |
252 | # Visual Studio code coverage results
253 | *.coverage
254 | *.coveragexml
255 |
256 | # NCrunch
257 | _NCrunch_*
258 | .*crunch*.local.xml
259 | nCrunchTemp_*
260 |
261 | # MightyMoose
262 | *.mm.*
263 | AutoTest.Net/
264 |
265 | # Web workbench (sass)
266 | .sass-cache/
267 |
268 | # Installshield output folder
269 | [Ee]xpress/
270 |
271 | # DocProject is a documentation generator add-in
272 | DocProject/buildhelp/
273 | DocProject/Help/*.HxT
274 | DocProject/Help/*.HxC
275 | DocProject/Help/*.hhc
276 | DocProject/Help/*.hhk
277 | DocProject/Help/*.hhp
278 | DocProject/Help/Html2
279 | DocProject/Help/html
280 |
281 | # Click-Once directory
282 | publish/
283 |
284 | # Publish Web Output
285 | *.[Pp]ublish.xml
286 | *.azurePubxml
287 | # Note: Comment the next line if you want to checkin your web deploy settings,
288 | # but database connection strings (with potential passwords) will be unencrypted
289 | *.pubxml
290 | *.publishproj
291 |
292 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
293 | # checkin your Azure Web App publish settings, but sensitive information contained
294 | # in these scripts will be unencrypted
295 | PublishScripts/
296 |
297 | # NuGet Packages
298 | *.nupkg
299 | # NuGet Symbol Packages
300 | *.snupkg
301 | # The packages folder can be ignored because of Package Restore
302 | **/[Pp]ackages/*
303 | # except build/, which is used as an MSBuild target.
304 | !**/[Pp]ackages/build/
305 | # Uncomment if necessary however generally it will be regenerated when needed
306 | #!**/[Pp]ackages/repositories.config
307 | # NuGet v3's project.json files produces more ignorable files
308 | *.nuget.props
309 | *.nuget.targets
310 |
311 | # Microsoft Azure Build Output
312 | csx/
313 | *.build.csdef
314 |
315 | # Microsoft Azure Emulator
316 | ecf/
317 | rcf/
318 |
319 | # Windows Store app package directories and files
320 | AppPackages/
321 | BundleArtifacts/
322 | Package.StoreAssociation.xml
323 | _pkginfo.txt
324 | *.appx
325 | *.appxbundle
326 | *.appxupload
327 |
328 | # Visual Studio cache files
329 | # files ending in .cache can be ignored
330 | *.[Cc]ache
331 | # but keep track of directories ending in .cache
332 | !?*.[Cc]ache/
333 |
334 | # Others
335 | ClientBin/
336 | ~$*
337 | *~
338 | *.dbmdl
339 | *.dbproj.schemaview
340 | *.jfm
341 | *.pfx
342 | *.publishsettings
343 | orleans.codegen.cs
344 |
345 | # Including strong name files can present a security risk
346 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
347 | #*.snk
348 |
349 | # Since there are multiple workflows, uncomment next line to ignore bower_components
350 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
351 | #bower_components/
352 |
353 | # RIA/Silverlight projects
354 | Generated_Code/
355 |
356 | # Backup & report files from converting an old project file
357 | # to a newer Visual Studio version. Backup files are not needed,
358 | # because we have git ;-)
359 | _UpgradeReport_Files/
360 | Backup*/
361 | UpgradeLog*.XML
362 | UpgradeLog*.htm
363 | ServiceFabricBackup/
364 | *.rptproj.bak
365 |
366 | # SQL Server files
367 | *.mdf
368 | *.ldf
369 | *.ndf
370 |
371 | # Business Intelligence projects
372 | *.rdl.data
373 | *.bim.layout
374 | *.bim_*.settings
375 | *.rptproj.rsuser
376 | *- [Bb]ackup.rdl
377 | *- [Bb]ackup ([0-9]).rdl
378 | *- [Bb]ackup ([0-9][0-9]).rdl
379 |
380 | # Microsoft Fakes
381 | FakesAssemblies/
382 |
383 | # GhostDoc plugin setting file
384 | *.GhostDoc.xml
385 |
386 | # Node.js Tools for Visual Studio
387 | .ntvs_analysis.dat
388 | node_modules/
389 |
390 | # Visual Studio 6 build log
391 | *.plg
392 |
393 | # Visual Studio 6 workspace options file
394 | *.opt
395 |
396 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
397 | *.vbw
398 |
399 | # Visual Studio LightSwitch build output
400 | **/*.HTMLClient/GeneratedArtifacts
401 | **/*.DesktopClient/GeneratedArtifacts
402 | **/*.DesktopClient/ModelManifest.xml
403 | **/*.Server/GeneratedArtifacts
404 | **/*.Server/ModelManifest.xml
405 | _Pvt_Extensions
406 |
407 | # Paket dependency manager
408 | .paket/paket.exe
409 | paket-files/
410 |
411 | # FAKE - F# Make
412 | .fake/
413 |
414 | # CodeRush personal settings
415 | .cr/personal
416 |
417 | # Python Tools for Visual Studio (PTVS)
418 | __pycache__/
419 | *.pyc
420 |
421 | # Cake - Uncomment if you are using it
422 | # tools/**
423 | # !tools/packages.config
424 |
425 | # Tabs Studio
426 | *.tss
427 |
428 | # Telerik's JustMock configuration file
429 | *.jmconfig
430 |
431 | # BizTalk build output
432 | *.btp.cs
433 | *.btm.cs
434 | *.odx.cs
435 | *.xsd.cs
436 |
437 | # OpenCover UI analysis results
438 | OpenCover/
439 |
440 | # Azure Stream Analytics local run output
441 | ASALocalRun/
442 |
443 | # MSBuild Binary and Structured Log
444 | *.binlog
445 |
446 | # NVidia Nsight GPU debugger configuration file
447 | *.nvuser
448 |
449 | # MFractors (Xamarin productivity tool) working folder
450 | .mfractor/
451 |
452 | # Local History for Visual Studio
453 | .localhistory/
454 |
455 | # BeatPulse healthcheck temp database
456 | healthchecksdb
457 |
458 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
459 | MigrationBackup/
460 |
461 | # Ionide (cross platform F# VS Code tools) working folder
462 | .ionide/
463 |
464 | # End of https://www.toptal.com/developers/gitignore/api/go,vs,nextjs,react,dotenv
465 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ecom-Golang-Fiber-Nextjs
2 | >### Development Mode
3 | >### Backend:
4 | >#### Step 1 => go run main.go
Database migrations is done in this step.
5 | >#### Step 2 => go run database/seeder/seeder.go
Seed the database.
6 | >#### Step 3 => go run main.go
Run the backend in Dev mode.
7 | >### Frontend:
8 | >#### Step 1 => cd front
Change current directory to front.
9 | >#### Step 2 => npm i
Install npm dependencies.
10 | >#### Step 3 => npm run dev
Run the frontend in Dev mode.
11 |
12 | >### Production Mode
13 | >### Backend:
14 | >#### Step 1 => go run main.go
Migrations is done in this step.
15 | >#### Step 2 => go build
To Build the app.
16 | >#### Step 3 => ./'NameOfTheApp'
Run the backend in Prod mode.
17 | >### Frontend:
18 | >#### Step 1 => cd front
Change current directory to front.
19 | >#### Step 2 => npm i
Install npm dependencies.
20 | >#### Step 3 => npm run build
Build frontend.
21 | >#### Step 4 => npm run start
Run the frontend in Prod mode.
22 |
--------------------------------------------------------------------------------
/controllers/authController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "fmt"
5 | "github.com/go-playground/validator/v10"
6 | "gonextjs/database"
7 | "gonextjs/middlewares"
8 | "gonextjs/models"
9 | "strconv"
10 | "time"
11 |
12 | "github.com/dgrijalva/jwt-go"
13 | "github.com/gofiber/fiber/v2"
14 | "gorm.io/gorm"
15 | )
16 |
17 | func Register(c *fiber.Ctx) error {
18 | var body models.NewUser
19 | if err := c.BodyParser(&body); err != nil {
20 | return err
21 | }
22 |
23 | validate := validator.New()
24 | err := validate.Struct(body)
25 | if err != nil {
26 | for _, err := range err.(validator.ValidationErrors) {
27 | switch err.Tag() {
28 | case "email":
29 | return c.Status(422).JSON(fiber.Map{
30 | "message": fmt.Sprintf("%s address is invalid", err.Field()),
31 | })
32 | case "required":
33 | return c.Status(422).JSON(fiber.Map{
34 | "message": fmt.Sprintf("%s can not be empty", err.Field()),
35 | })
36 | default:
37 | return err
38 | }
39 | }
40 | }
41 | if body.Password != body.PasswordConfirm {
42 | return c.Status(500).JSON(fiber.Map{
43 | "message": "passwords do not match",
44 | })
45 | }
46 | var userType models.UserTypes
47 | userType.UserType = "Customer"
48 | database.DBConn.Find(&userType)
49 | user := models.User{
50 | FirstName: body.FirstName,
51 | LastName: body.LastName,
52 | Email: body.Email,
53 | TypeId: userType.ID,
54 | }
55 | user.SetPassword(body.Password)
56 | if err := database.DBConn.Preload("UserTypes").Create(&user).Error; err != nil {
57 | return c.Status(500).JSON(fiber.Map{
58 | "message": "Email is already registered!",
59 | })
60 | }
61 |
62 | payload := jwt.StandardClaims{
63 | Subject: strconv.Itoa(int(user.ID)),
64 | ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
65 | }
66 | token, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, payload).SignedString([]byte("secret"))
67 | cookie := fiber.Cookie{
68 | Name: "jwt",
69 | Value: token,
70 | Expires: time.Now().Add(time.Hour * 24),
71 | HTTPOnly: true,
72 | }
73 | c.Cookie(&cookie)
74 | return c.JSON(user)
75 | }
76 |
77 | func Login(c *fiber.Ctx) error {
78 | var body models.Login
79 | if err := c.BodyParser(&body); err != nil {
80 | return err
81 | }
82 |
83 | validate := validator.New()
84 | err := validate.Struct(body)
85 | if err != nil {
86 | for _, err := range err.(validator.ValidationErrors) {
87 | switch err.Tag() {
88 | case "email":
89 | return c.Status(422).JSON(fiber.Map{
90 | "message": fmt.Sprintf("%s address is invalid", err.Field()),
91 | })
92 | case "required":
93 | return c.Status(422).JSON(fiber.Map{
94 | "message": fmt.Sprintf("%s can not be empty", err.Field()),
95 | })
96 | case "gte":
97 | return c.Status(422).JSON(fiber.Map{
98 | "message": fmt.Sprintf("%s can not be less then %s", err.Field(), err.Param()),
99 | })
100 | default:
101 | return err
102 | }
103 | }
104 | }
105 |
106 | var user models.User
107 | database.DBConn.Where("email = ?", body.Email).First(&user)
108 | if user.ID == 0 {
109 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
110 | "message": "Invalid Credentials",
111 | })
112 | }
113 | if err := user.ComparePasswords(body.Password); err != nil {
114 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
115 | "message": "Invalid Credentials",
116 | })
117 | }
118 |
119 | payload := jwt.StandardClaims{
120 | Subject: strconv.Itoa(int(user.ID)),
121 | ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
122 | }
123 | token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, payload).SignedString([]byte("secret"))
124 | if err != nil {
125 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
126 | "message": "Invalid Credentials",
127 | })
128 | }
129 |
130 | cookie := fiber.Cookie{
131 | Name: "jwt",
132 | Value: token,
133 | Expires: time.Now().Add(time.Hour * 24),
134 | HTTPOnly: true,
135 | }
136 | c.Cookie(&cookie)
137 | return c.JSON(fiber.Map{
138 | "message": "success",
139 | })
140 | }
141 |
142 | func User(c *fiber.Ctx) error {
143 | id, _ := middlewares.GetUserId(c)
144 | var user models.User
145 | database.DBConn.Preload("UserTypes").Where("id = ?", id).First(&user)
146 | return c.JSON(user)
147 | }
148 |
149 | func Logout(c *fiber.Ctx) error {
150 | cookie := fiber.Cookie{
151 | Name: "jwt",
152 | Value: "",
153 | Expires: time.Now().Add(-time.Hour),
154 | HTTPOnly: true,
155 | }
156 |
157 | c.Cookie(&cookie)
158 | return c.JSON(fiber.Map{
159 | "message": "success",
160 | })
161 | }
162 |
163 | func UpdateInfo(c *fiber.Ctx) error {
164 | var body models.UpdateUser
165 | if err := c.BodyParser(&body); err != nil {
166 | return err
167 | }
168 | id, _ := middlewares.GetUserId(c)
169 |
170 | validate := validator.New()
171 | err := validate.Struct(body)
172 | if err != nil {
173 | for _, err := range err.(validator.ValidationErrors) {
174 | switch err.Tag() {
175 | case "email":
176 | if !(body.Email == "") {
177 | return c.Status(422).JSON(fiber.Map{
178 | "message": fmt.Sprintf("%s address is invalid", err.Field()),
179 | })
180 | }
181 | case "required":
182 | return c.Status(422).JSON(fiber.Map{
183 | "message": fmt.Sprintf("%s can not be empty", err.Field()),
184 | })
185 | default:
186 | return err
187 | }
188 | }
189 | }
190 |
191 | user := models.User{Model: gorm.Model{ID: id}}
192 | if body.FirstName != "" {
193 | user.FirstName = body.FirstName
194 | }
195 |
196 | if body.LastName != "" {
197 | user.LastName = body.LastName
198 | }
199 |
200 | if body.Email != "" {
201 | user.Email = body.Email
202 | }
203 | if err := database.DBConn.Model(&user).Updates(&user).Error; err != nil {
204 | return c.Status(500).JSON(fiber.Map{
205 | "message": "Email is already in use! try another email",
206 | })
207 | }
208 | return c.JSON(user)
209 | }
210 |
211 | func UpdatePassword(c *fiber.Ctx) error {
212 | id, _ := middlewares.GetUserId(c)
213 | var body models.UserPassword
214 | if err := c.BodyParser(&body); err != nil {
215 | return err
216 | }
217 |
218 | validate := validator.New()
219 | err := validate.Struct(body)
220 | if err != nil {
221 | for _, err := range err.(validator.ValidationErrors) {
222 | switch err.Tag() {
223 | case "required":
224 | return c.Status(422).JSON(fiber.Map{
225 | "message": fmt.Sprintf("%s can not be empty", err.Field()),
226 | })
227 | case "gte":
228 | return c.Status(422).JSON(fiber.Map{
229 | "message": fmt.Sprintf("%s can not be less then %s", err.Field(), err.Param()),
230 | })
231 | default:
232 | return err
233 | }
234 | }
235 | }
236 | var user models.User
237 | database.DBConn.Where("id = ?", id).First(&user)
238 | if err := user.ComparePasswords(body.CurrentPassword); err != nil {
239 | return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
240 | "message": "Invalid Credentials",
241 | })
242 | }
243 | if body.Password != body.PasswordConfirm {
244 | return c.Status(500).JSON(fiber.Map{
245 | "message": "passwords did not match",
246 | })
247 | }
248 |
249 | updateUser := models.User{
250 | Model: gorm.Model{ID: id},
251 | }
252 | updateUser.SetPassword(body.Password)
253 | database.DBConn.Model(&updateUser).Updates(&updateUser)
254 | return c.JSON(fiber.Map{
255 | "message": "Password Updated",
256 | })
257 | }
258 |
--------------------------------------------------------------------------------
/controllers/categoryController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | "gonextjs/database"
6 | "gonextjs/models"
7 | )
8 |
9 | func AddCategory(c *fiber.Ctx) error {
10 | var data map[string]string
11 | if err := c.BodyParser(&data); err != nil {
12 | return err
13 | }
14 | if data["description"] == "" {
15 | return c.Status(500).JSON(fiber.Map{
16 | "message": "description is required!",
17 | })
18 | }
19 | category := models.Categories{
20 | Description: data["description"],
21 | }
22 | if err := database.DBConn.Create(&category).Error; err != nil {
23 | return c.Status(500).JSON(fiber.Map{
24 | "message": "Error",
25 | })
26 | }
27 |
28 | return c.JSON(category)
29 | }
30 |
31 | func GetAllCategories(c *fiber.Ctx) error {
32 | var categories []models.Categories
33 | database.DBConn.Find(&categories)
34 | return c.JSON(categories)
35 | }
36 |
--------------------------------------------------------------------------------
/controllers/currencyController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | "gonextjs/database"
6 | "gonextjs/models"
7 | )
8 |
9 | func AddCurrency(c *fiber.Ctx) error {
10 | var data map[string]string
11 | if err := c.BodyParser(&data); err != nil {
12 | return err
13 | }
14 | if data["title"] == "" {
15 | return c.Status(500).JSON(fiber.Map{
16 | "message": "title is required!",
17 | })
18 | }
19 | if data["code"] == "" {
20 | return c.Status(500).JSON(fiber.Map{
21 | "message": "code is required!",
22 | })
23 | }
24 | currency := models.Currency{
25 | Title: data["title"],
26 | Code: data["code"],
27 | }
28 | if err := database.DBConn.Create(¤cy).Error; err != nil {
29 | return c.Status(500).JSON(fiber.Map{
30 | "message": "Error",
31 | })
32 | }
33 |
34 | return c.JSON(currency)
35 | }
36 |
37 | func GetAllCurrencies(c *fiber.Ctx) error {
38 | var currencies []models.Currency
39 | database.DBConn.Find(¤cies)
40 | return c.JSON(currencies)
41 | }
42 |
--------------------------------------------------------------------------------
/controllers/productController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | "gonextjs/database"
6 | "gonextjs/models"
7 | "gonextjs/util"
8 | "math"
9 | "strconv"
10 | )
11 |
12 | type ProductsList struct {
13 | Products []models.Product
14 | FirstPage int
15 | CurrentPage int
16 | LastPage int
17 | }
18 |
19 | func GetAllProducts(c *fiber.Ctx) error {
20 | page, _ := strconv.Atoi(c.Query("page", "1"))
21 | pageSize, _ := strconv.Atoi(c.Query("pageSize", "10"))
22 | switch {
23 | case pageSize > 20:
24 | pageSize = 20
25 | case pageSize <= 0:
26 | pageSize = 10
27 | }
28 | offset := (page - 1) * pageSize
29 |
30 | var products ProductsList
31 | var totalRows int64
32 | var totalPage int
33 |
34 | database.DBConn.Model(models.Product{}).Count(&totalRows)
35 | totalPage = int(math.Ceil(float64(totalRows) / float64(pageSize)))
36 |
37 | products.FirstPage = 1
38 | products.CurrentPage = page
39 | products.LastPage = totalPage
40 |
41 | database.DBConn.Offset(offset).Limit(pageSize).Preload("User").Preload("Categories").Preload("Store").Preload("Currency").Find(&products.Products)
42 | return c.JSON(products)
43 | }
44 |
45 | func GetProduct(c *fiber.Ctx) error {
46 | id, _ := c.ParamsInt("id")
47 | var product models.Product
48 | product.ID = uint(id)
49 | database.DBConn.Preload("User").Preload("Categories").Preload("Store").Preload("Currency").Find(&product)
50 | return c.JSON(product)
51 | }
52 |
53 | func UpdateProduct(c *fiber.Ctx) error {
54 | id, _ := c.ParamsInt("id")
55 | product := models.Product{}
56 | if err := c.BodyParser(&product); err != nil {
57 | return err
58 | }
59 | product.ID = uint(id)
60 | database.DBConn.Model(&product).Updates(&product)
61 | return c.JSON(product)
62 | }
63 |
64 | func DeleteProduct(c *fiber.Ctx) error {
65 | id, _ := c.ParamsInt("id")
66 | var product models.Product
67 | product.ID = uint(id)
68 | //database.DBConn.Model(&product).Delete(&product)
69 | database.DBConn.Delete(&product)
70 | return nil
71 | }
72 |
73 | func AddProducts(c *fiber.Ctx) error {
74 | var data map[string]string
75 | if err := c.BodyParser(&data); err != nil {
76 | return err
77 | }
78 | if data["title"] == "" {
79 | return c.Status(500).JSON(fiber.Map{
80 | "message": "title is required!",
81 | })
82 | }
83 | if data["description"] == "" {
84 | return c.Status(500).JSON(fiber.Map{
85 | "message": "description is required!",
86 | })
87 | }
88 | if data["weight"] == "" {
89 | return c.Status(500).JSON(fiber.Map{
90 | "message": "weight is required!",
91 | })
92 | }
93 | if data["length"] == "" {
94 | return c.Status(500).JSON(fiber.Map{
95 | "message": "length is required!",
96 | })
97 | }
98 | if data["width"] == "" {
99 | return c.Status(500).JSON(fiber.Map{
100 | "message": "width is required!",
101 | })
102 | }
103 | if data["height"] == "" {
104 | return c.Status(500).JSON(fiber.Map{
105 | "message": "height is required!",
106 | })
107 | }
108 | if data["price"] == "" {
109 | return c.Status(500).JSON(fiber.Map{
110 | "message": "price is required!",
111 | })
112 | }
113 | if data["additional_price"] == "" {
114 | return c.Status(500).JSON(fiber.Map{
115 | "message": "additional_price is required!",
116 | })
117 | }
118 | if data["qty"] == "" {
119 | return c.Status(500).JSON(fiber.Map{
120 | "message": "qty is required!",
121 | })
122 | }
123 | if data["user_id"] == "" {
124 | return c.Status(500).JSON(fiber.Map{
125 | "message": "user_id is required!",
126 | })
127 | }
128 | if data["store_id"] == "" {
129 | return c.Status(500).JSON(fiber.Map{
130 | "message": "store_id is required!",
131 | })
132 | }
133 | if data["categories_id"] == "" {
134 | return c.Status(500).JSON(fiber.Map{
135 | "message": "categories_id is required!",
136 | })
137 | }
138 | if data["currency_id"] == "" {
139 | return c.Status(500).JSON(fiber.Map{
140 | "message": "currency_id is required!",
141 | })
142 | }
143 | product := models.Product{
144 | Title: data["title"],
145 | Description: data["description"],
146 | Weight: util.ParseFloat32(data["weight"]),
147 | Length: util.ParseFloat32(data["length"]),
148 | Width: util.ParseFloat32(data["width"]),
149 | Height: util.ParseFloat32(data["height"]),
150 | Price: util.ParseFloat32(data["price"]),
151 | AdditionalPrice: util.ParseFloat32(data["additional_price"]),
152 | QTY: util.ParseInt(data["qty"]),
153 | UserID: uint(util.ParseInt(data["user_id"])),
154 | StoreID: uint(util.ParseInt(data["store_id"])),
155 | CategoriesID: uint(util.ParseInt(data["categories_id"])),
156 | CurrencyID: uint(util.ParseInt(data["currency_id"])),
157 | }
158 | if err := database.DBConn.Create(&product).Error; err != nil {
159 | return c.Status(500).JSON(fiber.Map{
160 | "message": err,
161 | })
162 | }
163 |
164 | return c.JSON(product)
165 | }
166 |
--------------------------------------------------------------------------------
/controllers/storeController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | "gonextjs/database"
6 | "gonextjs/models"
7 | )
8 |
9 | func AddStore(c *fiber.Ctx) error {
10 | var data map[string]string
11 | if err := c.BodyParser(&data); err != nil {
12 | return err
13 | }
14 | if data["name"] == "" {
15 | return c.Status(500).JSON(fiber.Map{
16 | "message": "name is required!",
17 | })
18 | }
19 | if data["location"] == "" {
20 | return c.Status(500).JSON(fiber.Map{
21 | "message": "location is required!",
22 | })
23 | }
24 | store := models.Store{
25 | Name: data["name"],
26 | Location: data["location"],
27 | }
28 | if err := database.DBConn.Create(&store).Error; err != nil {
29 | return c.Status(500).JSON(fiber.Map{
30 | "message": "Error",
31 | })
32 | }
33 |
34 | return c.JSON(store)
35 | }
36 |
37 | func GetAllStores(c *fiber.Ctx) error {
38 | var stores []models.Store
39 | database.DBConn.Find(&stores)
40 | return c.JSON(stores)
41 | }
42 |
--------------------------------------------------------------------------------
/controllers/userController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
--------------------------------------------------------------------------------
/database/database.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "fmt"
5 | "gonextjs/models"
6 |
7 | "gorm.io/driver/postgres"
8 | "gorm.io/gorm"
9 | )
10 |
11 | var (
12 | DBConn *gorm.DB
13 | )
14 |
15 | func Connect() {
16 | dsn := "host=localhost user=postgres password=root dbname=nextlearn port=5432 sslmode=disable TimeZone=Asia/Shanghai"
17 | var err error
18 | DBConn, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
19 | SkipDefaultTransaction: true,
20 | })
21 | if err != nil {
22 | panic("Failed to connect to database")
23 | }
24 | fmt.Println("Database connection successfully opened")
25 | }
26 | func AutoMigrate() {
27 | err := DBConn.AutoMigrate(models.UserTypes{}, models.User{}, models.Store{}, models.Currency{}, models.Categories{}, models.Product{}, models.ProductsSold{}, models.Order{}, models.Cart{}, models.CartItems{}, models.Coupons{}, models.Reviews{})
28 | if err != nil {
29 | return
30 | }
31 | fmt.Println("Database Migrated🎉")
32 | }
33 |
--------------------------------------------------------------------------------
/database/seeder/seeder.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/bxcodec/faker/v3"
5 | "gonextjs/database"
6 | "gonextjs/models"
7 | "math/rand"
8 | )
9 |
10 | func main() {
11 | database.Connect()
12 | var users []models.User
13 | database.DBConn.Find(&users)
14 | if len(users) == 0 {
15 | for i := 0; i < 2; i++ {
16 | store := models.Store{
17 | Name: faker.Name(),
18 | Location: faker.Sentence(),
19 | }
20 | database.DBConn.Create(&store)
21 | }
22 |
23 | UserType0 := models.UserTypes{
24 | UserType: "Customer",
25 | }
26 | UserType1 := models.UserTypes{
27 | UserType: "Seller",
28 | }
29 | UserType2 := models.UserTypes{
30 | UserType: "Admin",
31 | }
32 | database.DBConn.Create(&UserType0)
33 | database.DBConn.Create(&UserType1)
34 | database.DBConn.Create(&UserType2)
35 |
36 | for i := 0; i < 2; i++ {
37 | categories := models.Categories{
38 | Description: faker.Sentence(),
39 | }
40 | database.DBConn.Create(&categories)
41 | }
42 | currency := models.Currency{
43 | Title: "USD",
44 | Code: "$",
45 | }
46 | database.DBConn.Create(¤cy)
47 |
48 | var userTypes models.UserTypes
49 | userTypes.UserType = "Customer"
50 | database.DBConn.Find(&userTypes)
51 | for i := 0; i < 100; i++ {
52 | user := models.User{
53 | FirstName: faker.FirstName(),
54 | LastName: faker.LastName(),
55 | Email: faker.Email(),
56 | TypeId: userTypes.ID,
57 | }
58 | user.SetPassword("password")
59 | database.DBConn.Create(&user)
60 | var stores []models.Store
61 | var categories []models.Store
62 | var currencies []models.Currency
63 | database.DBConn.Find(&stores)
64 | database.DBConn.Find(&categories)
65 | database.DBConn.Find(¤cies)
66 | product := models.Product{
67 | Title: faker.Name(),
68 | Description: faker.Paragraph(),
69 | Width: rand.Float32(),
70 | Length: rand.Float32(),
71 | Height: rand.Float32(),
72 | Image: faker.URL(),
73 | Price: float32(rand.Intn(90) + 10),
74 | AdditionalPrice: float32(rand.Intn(90) + 10),
75 | QTY: 3,
76 | UserID: user.ID,
77 | StoreID: stores[0].ID,
78 | CategoriesID: categories[0].ID,
79 | CurrencyID: currencies[0].ID,
80 | }
81 | database.DBConn.Create(&product)
82 | for i := 0; i < 2; i++ {
83 | review := models.Reviews{
84 | Ratting: 5,
85 | Body: faker.Paragraph(),
86 | UserID: user.ID,
87 | ProductID: product.ID,
88 | }
89 | database.DBConn.Create(&review)
90 | }
91 | product2 := models.Product{
92 | Title: faker.Name(),
93 | Description: faker.Paragraph(),
94 | Width: rand.Float32(),
95 | Length: rand.Float32(),
96 | Height: rand.Float32(),
97 | Image: faker.URL(),
98 | Price: float32(rand.Intn(90) + 10),
99 | AdditionalPrice: float32(rand.Intn(90) + 10),
100 | QTY: 3,
101 | UserID: user.ID,
102 | StoreID: stores[1].ID,
103 | CategoriesID: categories[1].ID,
104 | CurrencyID: currencies[0].ID,
105 | }
106 | database.DBConn.Create(&product2)
107 | for i := 0; i < 2; i++ {
108 | review := models.Reviews{
109 | Ratting: 5,
110 | Body: faker.Paragraph(),
111 | UserID: user.ID,
112 | ProductID: product2.ID,
113 | }
114 | database.DBConn.Create(&review)
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/front/.env.example:
--------------------------------------------------------------------------------
1 | PORT=8080
2 | NODE_ENV="development"
3 | NEXT_PUBLIC_BASE="/ecom_fiber"
4 | NEXT_PUBLIC_BASE_API="http://localhost:3000/api/"
--------------------------------------------------------------------------------
/front/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/front/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 |
40 | # Package-lock.json & yarn.lock
41 | package-lock.json
42 | yarn.lock
--------------------------------------------------------------------------------
/front/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/front/components/cart.tsx:
--------------------------------------------------------------------------------
1 | import { useDispatch, useSelector } from "react-redux";
2 | import { RootState } from "../store/store";
3 | import { FormEvent, useEffect, useState } from "react";
4 | import axios from "../util/axios";
5 | import { useRouter } from "next/router";
6 | import { showCart } from "../store/toggleCart";
7 | import { ProductState } from "../store/products";
8 | import {
9 | addItemToCart,
10 | deleteItemFromCart,
11 | removeItemFromCart,
12 | } from "../store/cart";
13 | import { MinusIcon, PlusIcon } from "@heroicons/react/solid";
14 |
15 | export default function Cart() {
16 | const router = useRouter();
17 | const items = useSelector((state: RootState) => state.cart.items);
18 | const show = useSelector((state: RootState) => state.toggleCart.Toggle);
19 | const total = useSelector((state: RootState) => state.cart.total);
20 | const dispatch = useDispatch();
21 | const [products, setProducts]: [any, any] = useState([]);
22 | useEffect(() => {
23 | if (!router.isReady) return;
24 | setProducts([]);
25 | items.map((item) => {
26 | axios
27 | .get(`products/${item?.ID}`)
28 | .then((res) => {
29 | let userData: ProductState = res.data;
30 | // @ts-ignore
31 | userData.QTY = item.QTY;
32 | setProducts((product: any) => [...product, userData]);
33 | })
34 | .catch((error) => error);
35 | });
36 | }, [items, router.isReady]);
37 | const addToCart = (event: FormEvent, id: number, price: number) => {
38 | event.preventDefault();
39 | dispatch(addItemToCart({ ID: id, QTY: 1, price }));
40 | };
41 |
42 | const removeFromCart = (event: FormEvent, id: number) => {
43 | event.preventDefault();
44 | dispatch(removeItemFromCart(id));
45 | };
46 |
47 | const deleteItem = (event: FormEvent, id: number) => {
48 | event.preventDefault();
49 | dispatch(deleteItemFromCart(id));
50 | };
51 |
52 | return (
53 | <>
54 | {show && (
55 |
59 |
63 |
64 |
190 |
191 |
192 |
193 |
194 | Summary
195 |
196 |
197 |
198 | Subtotal
199 |
200 |
201 | ${total}
202 |
203 |
204 | {/*
*/}
205 | {/*
*/}
206 | {/* Shipping*/}
207 | {/*
*/}
208 | {/*
*/}
209 | {/* $30*/}
210 | {/*
*/}
211 | {/*
*/}
212 | {/*
*/}
213 | {/*
*/}
214 | {/* Tax*/}
215 | {/*
*/}
216 | {/*
*/}
217 | {/* $35*/}
218 | {/*
*/}
219 | {/*
*/}
220 |
221 |
222 |
223 |
224 | Total
225 |
226 |
227 | ${total}
228 |
229 |
230 |
dispatch(showCart(false))}
232 | className="text-base leading-none w-full py-5 bg-gray-800 border-gray-800 border focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-800 text-white"
233 | >
234 | Checkout
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 | )}
243 | >
244 | );
245 | }
246 |
--------------------------------------------------------------------------------
/front/components/layout/base.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Nav from "./nav";
3 |
4 | interface Props {
5 | children: any
6 | }
7 |
8 | function Base({children}: Props) {
9 | return (
10 | <>
11 |
12 | {children}
13 | >
14 | );
15 | }
16 |
17 | export default Base;
--------------------------------------------------------------------------------
/front/components/layout/nav.tsx:
--------------------------------------------------------------------------------
1 | import { FormEvent, Fragment, useState } from "react";
2 | import { Dialog, Popover, Tab, Transition } from "@headlessui/react";
3 | import { MenuIcon, ShoppingBagIcon, XIcon } from "@heroicons/react/outline";
4 | import Link from "next/link";
5 | import { useDispatch, useSelector } from "react-redux";
6 | import { RootState } from "../../store/store";
7 | import axios from "../../util/axios";
8 | import { auth_login } from "../../store/userAuth";
9 | import { AxiosError } from "axios";
10 | import { showCart } from "../../store/toggleCart";
11 | const navigation = {
12 | categories: [
13 | // {
14 | // id: "women",
15 | // name: "Women",
16 | // featured: [
17 | // {
18 | // name: "New Arrivals",
19 | // href: "#",
20 | // imageSrc:
21 | // "https://tailwindui.com/img/ecommerce-images/mega-menu-category-01.jpg",
22 | // imageAlt:
23 | // "Models sitting back to back, wearing Basic Tee in black and bone.",
24 | // },
25 | // {
26 | // name: "Basic Tees",
27 | // href: "#",
28 | // imageSrc:
29 | // "https://tailwindui.com/img/ecommerce-images/mega-menu-category-02.jpg",
30 | // imageAlt:
31 | // "Close up of Basic Tee fall bundle with off-white, ochre, olive, and black tees.",
32 | // },
33 | // ],
34 | // sections: [
35 | // {
36 | // id: "clothing",
37 | // name: "Clothing",
38 | // items: [
39 | // { name: "Tops", href: "#" },
40 | // { name: "Dresses", href: "#" },
41 | // { name: "Pants", href: "#" },
42 | // { name: "Denim", href: "#" },
43 | // { name: "Sweaters", href: "#" },
44 | // { name: "T-Shirts", href: "#" },
45 | // { name: "Jackets", href: "#" },
46 | // { name: "Activewear", href: "#" },
47 | // { name: "Browse All", href: "#" },
48 | // ],
49 | // },
50 | // {
51 | // id: "accessories",
52 | // name: "Accessories",
53 | // items: [
54 | // { name: "Watches", href: "#" },
55 | // { name: "Wallets", href: "#" },
56 | // { name: "Bags", href: "#" },
57 | // { name: "Sunglasses", href: "#" },
58 | // { name: "Hats", href: "#" },
59 | // { name: "Belts", href: "#" },
60 | // ],
61 | // },
62 | // {
63 | // id: "brands",
64 | // name: "Brands",
65 | // items: [
66 | // { name: "Full Nelson", href: "#" },
67 | // { name: "My Way", href: "#" },
68 | // { name: "Re-Arranged", href: "#" },
69 | // { name: "Counterfeit", href: "#" },
70 | // { name: "Significant Other", href: "#" },
71 | // ],
72 | // },
73 | // ],
74 | // },
75 | // {
76 | // id: "men",
77 | // name: "Men",
78 | // featured: [
79 | // {
80 | // name: "New Arrivals",
81 | // href: "#",
82 | // imageSrc:
83 | // "https://tailwindui.com/img/ecommerce-images/product-page-04-detail-product-shot-01.jpg",
84 | // imageAlt:
85 | // "Drawstring top with elastic loop closure and textured interior padding.",
86 | // },
87 | // {
88 | // name: "Artwork Tees",
89 | // href: "#",
90 | // imageSrc:
91 | // "https://tailwindui.com/img/ecommerce-images/category-page-02-image-card-06.jpg",
92 | // imageAlt:
93 | // "Three shirts in gray, white, and blue arranged on table with same line drawing of hands and shapes overlapping on front of shirt.",
94 | // },
95 | // ],
96 | // sections: [
97 | // {
98 | // id: "clothing",
99 | // name: "Clothing",
100 | // items: [
101 | // { name: "Tops", href: "#" },
102 | // { name: "Pants", href: "#" },
103 | // { name: "Sweaters", href: "#" },
104 | // { name: "T-Shirts", href: "#" },
105 | // { name: "Jackets", href: "#" },
106 | // { name: "Activewear", href: "#" },
107 | // { name: "Browse All", href: "#" },
108 | // ],
109 | // },
110 | // {
111 | // id: "accessories",
112 | // name: "Accessories",
113 | // items: [
114 | // { name: "Watches", href: "#" },
115 | // { name: "Wallets", href: "#" },
116 | // { name: "Bags", href: "#" },
117 | // { name: "Sunglasses", href: "#" },
118 | // { name: "Hats", href: "#" },
119 | // { name: "Belts", href: "#" },
120 | // ],
121 | // },
122 | // {
123 | // id: "brands",
124 | // name: "Brands",
125 | // items: [
126 | // { name: "Re-Arranged", href: "#" },
127 | // { name: "Counterfeit", href: "#" },
128 | // { name: "Full Nelson", href: "#" },
129 | // { name: "My Way", href: "#" },
130 | // ],
131 | // },
132 | // ],
133 | // },
134 | ],
135 | pages: [
136 | { name: "Home", href: "/" },
137 | { name: "Shop", href: "/shop" },
138 | ],
139 | };
140 |
141 | function classNames(...classes: any) {
142 | return classes.filter(Boolean).join(" ");
143 | }
144 |
145 | export default function Nav() {
146 | const auth = useSelector((state: RootState) => state.userAuth.loggedIn);
147 | const cartLength = useSelector((state: RootState) => state.cart.length);
148 | const dispatch = useDispatch();
149 | const [open, setOpen] = useState(false);
150 |
151 | const signOut = (event: FormEvent) => {
152 | event.preventDefault();
153 | axios
154 | .get("admin/logout")
155 | .then((res) => {
156 | dispatch(auth_login(false));
157 | })
158 | .catch((error: AxiosError) => {
159 | if (error.response?.status === 401) dispatch(auth_login(false));
160 | });
161 | };
162 |
163 | return (
164 |
165 | {/* Mobile menu */}
166 |
167 |
172 |
181 |
182 |
183 |
184 |
193 |
194 |
195 | setOpen(false)}
199 | >
200 | Close menu
201 |
202 |
203 |
204 |
205 | {/* Links */}
206 |
207 |
208 |
209 | {/*{navigation.categories &&*/}
210 | {/* navigation.categories.map((category) => (*/}
211 | {/* */}
214 | {/* classNames(*/}
215 | {/* selected*/}
216 | {/* ? "text-indigo-600 border-indigo-600"*/}
217 | {/* : "text-gray-900 border-transparent",*/}
218 | {/* "flex-1 whitespace-nowrap py-4 px-1 border-b-2 text-base font-medium"*/}
219 | {/* )*/}
220 | {/* }*/}
221 | {/* >*/}
222 | {/* {category.name}*/}
223 | {/* */}
224 | {/* ))}*/}
225 |
226 |
227 |
228 | {/*{navigation.categories &&*/}
229 | {/* navigation.categories.map((category) => (*/}
230 | {/* */}
234 | {/* */}
235 | {/* {category.featured.map((item) => (*/}
236 | {/*
*/}
261 | {/* ))}*/}
262 | {/*
*/}
263 | {/* {category.sections.map((section) => (*/}
264 | {/* */}
265 | {/*
*/}
269 | {/* {section.name}*/}
270 | {/*
*/}
271 | {/*
*/}
287 | {/*
*/}
288 | {/* ))}*/}
289 | {/* */}
290 | {/* ))}*/}
291 |
292 |
293 |
294 |
295 | {navigation &&
296 | navigation.pages.map((page) => (
297 |
298 |
302 | {page.name}
303 |
304 |
305 | ))}
306 |
307 |
308 |
326 |
327 |
340 |
341 |
342 |
343 |
344 |
345 |
583 |
584 | );
585 | }
586 |
--------------------------------------------------------------------------------
/front/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/front/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | basePath: process.env.NEXT_PUBLIC_BASE,
4 | reactStrictMode: true,
5 | images: {
6 | domains: ["images.unsplash.com", "tailwindui.com"],
7 | },
8 | publicRuntimeConfig: {
9 | // Will be available on both server and client
10 | backendUrl: process.env.NEXT_PUBLIC_BASE_API,
11 | appMode: process.env.NODE_ENV,
12 | },
13 | };
14 |
15 | module.exports = nextConfig;
16 |
--------------------------------------------------------------------------------
/front/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "front",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "node server.js",
7 | "build": "next build",
8 | "start": "cross-env NODE_ENV=production node server.js"
9 | },
10 | "dependencies": {
11 | "@headlessui/react": "^1.5.0",
12 | "@heroicons/react": "^1.0.6",
13 | "@reduxjs/toolkit": "^1.8.5",
14 | "@tailwindcss/aspect-ratio": "^0.4.0",
15 | "@tailwindcss/forms": "^0.5.3",
16 | "axios": "^0.26.1",
17 | "cookies-next": "^2.1.1",
18 | "cross-env": "^7.0.2",
19 | "dotenv": "^16.0.1",
20 | "express": "^4.17.1",
21 | "next": "13.2.3",
22 | "next-redux-wrapper": "^8.0.0",
23 | "react": "18.2.0",
24 | "react-dom": "18.2.0",
25 | "react-redux": "^8.0.2",
26 | "redux-persist": "^6.0.0",
27 | "sharp": "^0.30.2"
28 | },
29 | "devDependencies": {
30 | "@types/node": "17.0.21",
31 | "@types/react": "17.0.39",
32 | "@types/redux-persist": "^4.3.1",
33 | "autoprefixer": "^10.4.2",
34 | "eslint": "8.10.0",
35 | "eslint-config-next": "13.2.3",
36 | "postcss": "^8.4.8",
37 | "prettier": "2.5.1",
38 | "tailwindcss": "^3.0.23",
39 | "typescript": "4.6.2"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/front/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "../styles/globals.css";
2 | import Base from "../components/layout/base";
3 | import { persistor, store } from "../store/store";
4 | import { Provider, useDispatch } from "react-redux";
5 | import { AppProps } from "next/app";
6 | import { PersistGate } from "redux-persist/integration/react";
7 | import Cart from "../components/cart";
8 | import { useEffect } from "react";
9 | import { showCart } from "../store/toggleCart";
10 |
11 | function MyApp({ Component, pageProps }: AppProps) {
12 | useEffect(() => {
13 | store.dispatch(showCart(false));
14 | });
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | }
27 |
28 | export default MyApp;
29 |
--------------------------------------------------------------------------------
/front/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import { getCookie } from "cookies-next";
3 | import { useEffect } from "react";
4 |
5 | interface Props {
6 | status: string;
7 | }
8 |
9 | const features = [
10 | { name: "Origin", description: "Designed by Good Goods, Inc." },
11 | {
12 | name: "Material",
13 | description:
14 | "Solid walnut base with rare earth magnets and powder coated steel card cover",
15 | },
16 | { name: "Dimensions", description: '6.25" x 3.55" x 1.15"' },
17 | { name: "Finish", description: "Hand sanded and finished with natural oil" },
18 | { name: "Includes", description: "Wood card tray and 3 refill packs" },
19 | {
20 | name: "Considerations",
21 | description:
22 | "Made from natural materials. Grain and color vary with each item.",
23 | },
24 | ];
25 |
26 | const Home = (props: Props) => {
27 | return (
28 | <>
29 |
30 |
31 |
32 |
33 |
34 | Summer styles are finally here
35 |
36 |
37 | This year, our new summer collection will shelter you from the
38 | harsh elements of a world that doesn`'`t care if you live
39 | or die.
40 |
41 |
42 |
43 |
44 | {/* Decorative image grid */}
45 |
49 |
50 |
51 |
52 |
53 |
60 |
61 |
62 |
69 |
70 |
71 |
72 |
73 |
80 |
81 |
82 |
89 |
90 |
91 |
98 |
99 |
100 |
101 |
102 |
109 |
110 |
111 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
128 | Shop Collection
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | Technical Specifications
140 |
141 |
142 | The walnut wood card tray is precision milled to perfectly fit a
143 | stack of Focus cards. The powder coated steel divider separates
144 | active cards from new ones, or can be used to archive important
145 | task lists.
146 |
147 |
148 |
149 | {features.map((feature) => (
150 |
154 |
{feature.name}
155 |
156 | {feature.description}
157 |
158 |
159 | ))}
160 |
161 |
162 |
163 |
168 |
173 |
178 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
E-Com
191 |
192 |
193 | Copyright © 2022 E-Com
194 |
195 |
196 | All rights reserved
197 |
198 |
199 |
215 |
231 |
247 |
263 |
264 |
265 |
266 |
267 | Company
268 |
269 |
270 | Blog
271 |
272 |
273 | Pricing
274 |
275 |
276 | About Us
277 |
278 |
279 | Contact us
280 |
281 |
282 | Testimonials
283 |
284 |
285 |
286 |
287 | Support
288 |
289 |
290 | Legal policy
291 |
292 |
293 | Status policy
294 |
295 |
296 | Privacy policy
297 |
298 |
299 | Terms of service
300 |
301 |
302 |
303 |
304 | Get updates
305 |
306 |
307 |
312 |
320 |
324 |
325 |
326 |
327 |
328 |
329 |
330 | Get updates
331 |
332 |
354 |
355 |
356 | >
357 | );
358 | };
359 |
360 | export default Home;
361 |
--------------------------------------------------------------------------------
/front/pages/product/[id].tsx:
--------------------------------------------------------------------------------
1 | import { FormEvent, useEffect, useState } from "react";
2 | import { useRouter } from "next/router";
3 | import { StarIcon } from "@heroicons/react/solid";
4 | import { RadioGroup } from "@headlessui/react";
5 | import axios from "../../util/axios";
6 | import { addItemToCart } from "../../store/cart";
7 | import { useDispatch } from "react-redux";
8 | const breadcrumbs = [
9 | { id: 1, name: "Men", href: "#" },
10 | { id: 2, name: "Clothing", href: "#" },
11 | ];
12 | const product1 = {
13 | name: "Basic Tee 6-Pack",
14 | price: "$192",
15 | href: "#",
16 | images: [
17 | {
18 | src: "https://tailwindui.com/img/ecommerce-images/product-page-02-secondary-product-shot.jpg",
19 | alt: "Two each of gray, white, and black shirts laying flat.",
20 | },
21 | {
22 | src: "https://tailwindui.com/img/ecommerce-images/product-page-02-tertiary-product-shot-01.jpg",
23 | alt: "Model wearing plain black basic tee.",
24 | },
25 | {
26 | src: "https://tailwindui.com/img/ecommerce-images/product-page-02-tertiary-product-shot-02.jpg",
27 | alt: "Model wearing plain gray basic tee.",
28 | },
29 | {
30 | src: "https://tailwindui.com/img/ecommerce-images/product-page-02-featured-product-shot.jpg",
31 | alt: "Model wearing plain white basic tee.",
32 | },
33 | ],
34 | colors: [
35 | { name: "White", class: "bg-white", selectedClass: "ring-gray-400" },
36 | { name: "Gray", class: "bg-gray-200", selectedClass: "ring-gray-400" },
37 | { name: "Black", class: "bg-gray-900", selectedClass: "ring-gray-900" },
38 | ],
39 | sizes: [
40 | { name: "XXS", inStock: false },
41 | { name: "XS", inStock: true },
42 | { name: "S", inStock: true },
43 | { name: "M", inStock: true },
44 | { name: "L", inStock: true },
45 | { name: "XL", inStock: true },
46 | { name: "2XL", inStock: true },
47 | { name: "3XL", inStock: true },
48 | ],
49 | description:
50 | 'The Basic Tee 6-Pack allows you to fully express your vibrant personality with three grayscale options. Feeling adventurous? Put on a heather gray tee. Want to be a trendsetter? Try our exclusive colorway: "Black". Need to add an extra pop of color to your outfit? Our white tee has you covered.',
51 | highlights: [
52 | "Hand cut and sewn locally",
53 | "Dyed with our proprietary colors",
54 | "Pre-washed & pre-shrunk",
55 | "Ultra-soft 100% cotton",
56 | ],
57 | details:
58 | 'The 6-Pack includes two black, two white, and two heather gray Basic Tees. Sign up for our subscription service and be the first to get new, exciting colors, like our upcoming "Charcoal Gray" limited release.',
59 | };
60 | const reviews = { href: "#", average: 4, totalCount: 117 };
61 |
62 | function classNames(...classes: any) {
63 | return classes.filter(Boolean).join(" ");
64 | }
65 |
66 | export default function Product() {
67 | const router = useRouter();
68 | const dispatch = useDispatch();
69 |
70 | const [product, setProduct] = useState(undefined);
71 | const [selectedColor, setSelectedColor] = useState(product1.colors[0]);
72 | const [selectedSize, setSelectedSize] = useState(product1.sizes[2]);
73 |
74 | useEffect(() => {
75 | if (!router.isReady) return;
76 | const { id } = router.query;
77 | axios
78 | .get(`products/${id}`)
79 | .then((res) => {
80 | setProduct(res.data);
81 | })
82 | .catch((error) => error);
83 | }, [router.isReady, router.query]);
84 |
85 | const addToCart = (event: FormEvent, id: number, price: number) => {
86 | event.preventDefault();
87 | // const ID: CartItemState = { ID: id };
88 | dispatch(addItemToCart({ ID: id, QTY: 1, price }));
89 | };
90 |
91 | return (
92 |
93 |
94 |
95 |
99 | {breadcrumbs.map((breadcrumb) => (
100 |
101 |
120 |
121 | ))}
122 | {product && (
123 |
124 |
130 | {/* @ts-ignore */}
131 | {product.title}
132 |
133 |
134 | )}
135 |
136 |
137 |
138 | {/* Image gallery */}
139 |
140 |
141 |
146 |
147 |
148 |
149 |
154 |
155 |
156 |
161 |
162 |
163 |
164 |
169 |
170 |
171 |
172 | {/* Product info */}
173 |
174 |
175 |
176 | {/* @ts-ignore */}
177 | {product && product.title}
178 |
179 |
180 |
181 | {/* Options */}
182 |
183 |
Product information
184 |
185 | {product &&
186 | // @ts-ignore
187 | product.Currency.code +
188 | " " +
189 | // @ts-ignore
190 | (product.price + product.additionalPrice)}
191 |
192 |
193 | {/* Reviews */}
194 |
195 |
Reviews
196 |
197 |
198 | {[0, 1, 2, 3, 4].map((rating) => (
199 | rating
203 | ? "text-gray-900"
204 | : "text-gray-200",
205 | "h-5 w-5 flex-shrink-0"
206 | )}
207 | aria-hidden="true"
208 | />
209 | ))}
210 |
211 |
{reviews.average} out of 5 stars
212 |
216 | {reviews.totalCount} reviews
217 |
218 |
219 |
220 |
221 |
361 |
362 |
363 |
364 | {/* Description and details */}
365 |
366 |
Description
367 |
368 |
369 |
370 | {/* @ts-ignore */}
371 | {product && product.description}
372 |
373 |
374 |
375 |
376 |
377 |
Highlights
378 |
379 |
380 |
381 | {product1.highlights.map((highlight) => (
382 |
383 | {highlight}
384 |
385 | ))}
386 |
387 |
388 |
389 |
390 |
391 |
Details
392 |
393 |
394 |
{product1.details}
395 |
396 |
397 |
398 |
399 |
400 |
401 | );
402 | }
403 |
--------------------------------------------------------------------------------
/front/pages/shop.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | import { FormEvent, useEffect, useState } from "react";
4 | import axios from "../util/axios";
5 | import { PlusIcon } from "@heroicons/react/solid";
6 | import { useDispatch, useSelector } from "react-redux";
7 | import { RootState } from "../store/store";
8 | import { addProducts, ProductsState, ProductState } from "../store/products";
9 | import { addItemToCart, CartItemState } from "../store/cart";
10 | function Shop() {
11 | const { products }: ProductsState = useSelector(
12 | (state: RootState) => state.products
13 | );
14 | const dispatch = useDispatch();
15 | const [startPage, setStartPage] = useState(0);
16 | const [currentPage, setCurrentPage] = useState(0);
17 | const [lastPage, setLastPage] = useState(0);
18 | useEffect(() => {
19 | axios
20 | .get("products?page=1&pageSize=20")
21 | .then((res) => {
22 | dispatch(addProducts(res.data.Products));
23 | setStartPage(res.data.FirstPage);
24 | setCurrentPage(res.data.CurrentPage);
25 | setLastPage(res.data.LastPage);
26 | })
27 | .catch((error) => error);
28 | }, []);
29 |
30 | const fetchNewData = (page: number, event: Event) => {
31 | event.preventDefault();
32 |
33 | axios
34 | .get(`products?page=${page}&pageSize=20`)
35 | .then((res) => {
36 | dispatch(addProducts(res.data.Products));
37 | setStartPage(res.data.FirstPage);
38 | setCurrentPage(res.data.CurrentPage);
39 | setLastPage(res.data.LastPage);
40 | })
41 | .catch((error) => error);
42 | };
43 |
44 | const getPaginationList = () => {
45 | const rowList = [];
46 | if (currentPage > 2) {
47 | rowList.push(
48 |
49 | fetchNewData(1, event)}
52 | href=""
53 | className={`py-2 px-3 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700`}
54 | >
55 | {1}
56 |
57 |
58 | );
59 | }
60 | let numberOfIteration = 0;
61 | for (
62 | let i = lastPage - currentPage >= 3 ? currentPage - 1 : lastPage - 3;
63 | i <= lastPage;
64 | i++
65 | ) {
66 | if (numberOfIteration === 4) break;
67 | if (i === 0) continue;
68 | rowList.push(
69 |
70 | fetchNewData(i, event)}
73 | href=""
74 | className={`py-2 px-3 ${
75 | i === currentPage
76 | ? "text-blue-600 bg-blue-50"
77 | : "leading-tight text-gray-500 bg-white"
78 | } border border-gray-300 hover:bg-gray-100 hover:text-gray-700`}
79 | >
80 | {i}
81 |
82 |
83 | );
84 | numberOfIteration++;
85 | }
86 | if (lastPage - currentPage >= 3) {
87 | rowList.push(
88 |
89 | fetchNewData(lastPage, event)}
92 | href=""
93 | className={`py-2 px-3 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700`}
94 | >
95 | {lastPage}
96 |
97 |
98 | );
99 | }
100 | return rowList;
101 | };
102 |
103 | const addToCart = (event: FormEvent, id: number, price: number) => {
104 | event.preventDefault();
105 | // const ID: CartItemState = { ID: id };
106 | dispatch(addItemToCart({ ID: id, QTY: 1, price }));
107 | };
108 |
109 | return (
110 |
111 |
112 |
113 | Shop
114 |
115 |
116 |
117 | {products.length > 0 &&
118 | products.map(
119 | (product) =>
120 | product && (
121 |
170 | )
171 | )}
172 |
173 |
174 |
178 |
219 |
220 |
221 | );
222 | }
223 | // This gets called on every request
224 | // export async function getServerSideProps(context: GetServerSidePropsContext) {
225 | // // Fetch data from external API
226 | // const data = await axios.get("products?page=1&pageSize=10", {
227 | // headers: {
228 | // Cookie: `jwt=${context.req.cookies.jwt}`,
229 | // },
230 | // });
231 | //
232 | // // Pass data to the page via props
233 | // return { props: { data: data.data } };
234 | // }
235 |
236 | export default Shop;
237 |
--------------------------------------------------------------------------------
/front/pages/signin.tsx:
--------------------------------------------------------------------------------
1 | import { LockClosedIcon } from "@heroicons/react/solid";
2 | import { FormEvent, useLayoutEffect, useState } from "react";
3 | import axios from "../util/axios";
4 | import { useRouter } from "next/router";
5 | import { useDispatch, useSelector } from "react-redux";
6 | import { RootState } from "../store/store";
7 | import { auth_login } from "../store/userAuth";
8 |
9 | function Signin() {
10 | const auth = useSelector((state: RootState) => state.userAuth.loggedIn);
11 | const dispatch = useDispatch();
12 | const router = useRouter();
13 | const [email, setEmail] = useState(undefined);
14 | const [password, setPassword] = useState(undefined);
15 | useLayoutEffect(() => {
16 | if (auth) router.push("/");
17 | }, [auth, router, router.isReady]);
18 |
19 | const login = (event: FormEvent) => {
20 | event.preventDefault();
21 | axios
22 | .post("login", {
23 | email,
24 | password,
25 | })
26 | .then((res) => {
27 | dispatch(auth_login(true));
28 | router.push("/");
29 | })
30 | .catch((error) => error);
31 | };
32 | return (
33 | <>
34 |
35 |
36 |
37 |
42 |
43 | Sign in to your account
44 |
45 |
46 |
125 |
126 |
127 | >
128 | );
129 | }
130 |
131 | export default Signin;
132 |
--------------------------------------------------------------------------------
/front/pages/signup.tsx:
--------------------------------------------------------------------------------
1 | import { HandIcon } from "@heroicons/react/solid";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { RootState } from "../store/store";
4 | import { FormEvent, useLayoutEffect, useState } from "react";
5 | import { useRouter } from "next/router";
6 | import axios from "../util/axios";
7 | import { auth_login } from "../store/userAuth";
8 |
9 | export default function Signup() {
10 | const auth = useSelector((state: RootState) => state.userAuth.loggedIn);
11 | const dispatch = useDispatch();
12 | const router = useRouter();
13 | const [firstName, setFirstName] = useState("");
14 | const [lastName, setLastName] = useState("");
15 | const [email, setEmail] = useState("");
16 | const [password, setPassword] = useState("");
17 | const [confirmPassword, setConfirmPassword] = useState("");
18 | useLayoutEffect(() => {
19 | if (!router.isReady) return;
20 | if (auth) router.push("/");
21 | }, [auth, router, router.isReady]);
22 | const signUp = (event: FormEvent) => {
23 | event.preventDefault();
24 | if (firstName.trim() === "") alert("First Name cannot be empty!");
25 | else if (lastName.trim() === "") alert("Last Name cannot be empty!");
26 | else if (email.trim() === "") alert("Email cannot be empty!");
27 | else if (password.trim() === "") alert("Password cannot be empty!");
28 | else if (confirmPassword.trim() === "")
29 | alert("Confirm Password cannot be empty!");
30 | else if (password.trim() !== confirmPassword.trim())
31 | alert("Passwords did not match!");
32 | else {
33 | axios
34 | .post("register", {
35 | firstname: firstName.trim(),
36 | lastname: lastName.trim(),
37 | email: email.trim(),
38 | password: password.trim(),
39 | password_confirm: confirmPassword.trim(),
40 | })
41 | .then((res) => {
42 | dispatch(auth_login(true));
43 | router.push("/");
44 | })
45 | .catch((error) => error);
46 | }
47 | };
48 | return (
49 | <>
50 | {/*
51 | This example requires updating your template:
52 |
53 | ```
54 |
55 |
56 | ```
57 | */}
58 |
59 |
60 |
61 |
66 |
67 | Sign Up for an account
68 |
69 |
70 |
194 |
195 |
196 | >
197 | );
198 | }
199 |
--------------------------------------------------------------------------------
/front/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/front/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ozair0/Ecom-Golang-Fiber-Nextjs/26add39368026347dfc7117250eca38cfc0ebe6d/front/public/favicon.ico
--------------------------------------------------------------------------------
/front/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/front/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const next = require('next')
3 |
4 | require("dotenv").config();
5 |
6 | const port = process.env.PORT
7 | const dev = process.env.NODE_ENV !== 'production'
8 | const app = next({ dev })
9 | const handle = app.getRequestHandler()
10 |
11 | app.prepare().then(() => {
12 | const server = express()
13 |
14 | server.all(process.env.NEXT_PUBLIC_BASE+'*', (req, res) => {
15 | return handle(req, res)
16 | })
17 |
18 | server.listen(port, () => {
19 | console.log(`> Ready on http://localhost:${port}`)
20 | })
21 | })
--------------------------------------------------------------------------------
/front/store/cart.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2 |
3 | export interface CartItemState {
4 | ID: number;
5 | QTY: number;
6 | price: number;
7 | }
8 |
9 | export interface CartState {
10 | items: [CartItemState?];
11 | length: number;
12 | total: number;
13 | }
14 |
15 | const initialState: CartState = {
16 | items: [],
17 | length: 0,
18 | total: 0,
19 | };
20 |
21 | export const cartSlice = createSlice({
22 | name: "cart",
23 | initialState,
24 | reducers: {
25 | addItemToCart: (state: CartState, action: PayloadAction) => {
26 | if (action.payload.ID !== null) {
27 | state.length += 1;
28 | state.total += action.payload.price;
29 | let found = false;
30 | state.items.map((item) => {
31 | if (item?.ID === action.payload.ID) {
32 | item.QTY += 1;
33 | found = true;
34 | }
35 | });
36 | if (!found) {
37 | state.items.push(action.payload);
38 | state.items.sort(function (a, b) {
39 | // @ts-ignore
40 | return a.ID - b.ID || a.title.localeCompare(b.title);
41 | });
42 | }
43 | }
44 | },
45 | removeItemFromCart: (state: CartState, action: PayloadAction) => {
46 | // @ts-ignore
47 | state.items.map((item, index) => {
48 | if (item?.ID === action.payload) {
49 | state.length -= 1;
50 | state.total -= item.price;
51 | if (item.QTY > 1) {
52 | item.QTY -= 1;
53 | } else {
54 | // @ts-ignore
55 | state.items.splice(index, 1);
56 | }
57 | }
58 | });
59 | },
60 | deleteItemFromCart: (state: CartState, action: PayloadAction) => {
61 | // @ts-ignore
62 | state.items.map((item, index) => {
63 | if (item?.ID === action.payload) {
64 | state.length -= item.QTY;
65 | state.total -= item.price * item.QTY;
66 | // @ts-ignore
67 | state.items.splice(index, 1);
68 | }
69 | });
70 | },
71 | },
72 | });
73 |
74 | // Action creators are generated for each case reducer function
75 | export const { addItemToCart, removeItemFromCart, deleteItemFromCart } =
76 | cartSlice.actions;
77 |
78 | export default cartSlice.reducer;
79 |
--------------------------------------------------------------------------------
/front/store/products.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2 |
3 | export interface Currency {
4 | ID: number;
5 | CreatedAt: string;
6 | UpdatedAt: string;
7 | DeletedAt: any;
8 | title: string;
9 | code: string;
10 | }
11 | export interface Categories {
12 | ID: number;
13 | CreatedAt: string;
14 | UpdatedAt: string;
15 | DeletedAt: any;
16 | description: string;
17 | Product: any;
18 | }
19 | export interface Store {
20 | ID: number;
21 | CreatedAt: string;
22 | UpdatedAt: string;
23 | DeletedAt: any;
24 | name: string;
25 | location: string;
26 | Product: any;
27 | Cart: any;
28 | Order: any;
29 | }
30 | export interface User {
31 | ID: number;
32 | CreatedAt: string;
33 | UpdatedAt: string;
34 | DeletedAt: any;
35 | firstname: string;
36 | lastname: string;
37 | email: string;
38 | }
39 |
40 | export interface ProductState {
41 | ID: number;
42 | CreatedAt: string;
43 | UpdatedAt: string;
44 | DeletedAt: any;
45 | title: string;
46 | description: string;
47 | weight: number;
48 | length: number;
49 | width: number;
50 | height: number;
51 | price: number;
52 | additionalPrice: number;
53 | image: string;
54 | QTY: number;
55 | UserID: number;
56 | StoreID: number;
57 | CategoriesID: number;
58 | CurrencyID: number;
59 | Currency: Currency;
60 | Categories: Categories;
61 | Store: Store;
62 | User: User;
63 | }
64 |
65 | export interface ProductsState {
66 | products: [ProductState?];
67 | }
68 |
69 | const initialState: ProductsState = {
70 | products: [],
71 | };
72 |
73 | export const productSlice = createSlice({
74 | name: "product",
75 | initialState,
76 | reducers: {
77 | addProducts: (
78 | state: ProductsState,
79 | action: PayloadAction<[ProductState]>
80 | ) => {
81 | state.products = action.payload;
82 | },
83 | },
84 | });
85 |
86 | // Action creators are generated for each case reducer function
87 | export const { addProducts } = productSlice.actions;
88 |
89 | export default productSlice.reducer;
90 |
--------------------------------------------------------------------------------
/front/store/store.ts:
--------------------------------------------------------------------------------
1 | import { combineReducers, configureStore } from "@reduxjs/toolkit";
2 | import storage from "../util/storage";
3 | import userAuth from "./userAuth";
4 | import { persistReducer, persistStore } from "redux-persist";
5 | import getConfig from "next/config";
6 | import products from "./products";
7 | import cart from "./cart";
8 | import toggleCart from "./toggleCart";
9 | const { publicRuntimeConfig } = getConfig();
10 |
11 | const rootReducer = combineReducers({
12 | userAuth,
13 | products,
14 | cart,
15 | toggleCart,
16 | // any other reducers here
17 | });
18 | const persistConfig = {
19 | key: "root",
20 | version: 1,
21 | storage,
22 | };
23 | const persistedReducer = persistReducer(persistConfig, rootReducer);
24 |
25 | export const store = configureStore({
26 | reducer: persistedReducer,
27 | middleware: (getDefaultMiddleware) =>
28 | getDefaultMiddleware({
29 | serializableCheck: {
30 | ignoredActions: ["persist/PERSIST"],
31 | ignoredPaths: [],
32 | },
33 | }),
34 | devTools: publicRuntimeConfig.appMode === "development",
35 | });
36 |
37 | // Infer the `RootState` and `AppDispatch` types from the store itself
38 | export type RootState = ReturnType;
39 | // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
40 | export type AppDispatch = typeof store.dispatch;
41 | export let persistor = persistStore(store);
42 | // export const wrapper = createWrapper(() => store);
43 |
--------------------------------------------------------------------------------
/front/store/toggleCart.tsx:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2 |
3 | export interface ToggleCartState {
4 | Toggle: boolean;
5 | }
6 |
7 | const initialState: ToggleCartState = {
8 | Toggle: false,
9 | };
10 |
11 | export const toggleCartSlice = createSlice({
12 | name: "cart",
13 | initialState,
14 | reducers: {
15 | showCart: (state: ToggleCartState, action: PayloadAction) => {
16 | state.Toggle = action.payload;
17 | },
18 | },
19 | });
20 |
21 | // Action creators are generated for each case reducer function
22 | export const { showCart } = toggleCartSlice.actions;
23 |
24 | export default toggleCartSlice.reducer;
25 |
--------------------------------------------------------------------------------
/front/store/userAuth.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2 |
3 | export interface AuthState {
4 | loggedIn: boolean;
5 | }
6 |
7 | const initialState: AuthState = {
8 | loggedIn: false,
9 | };
10 |
11 | export const userAuthSlice = createSlice({
12 | name: "userAuth",
13 | initialState,
14 | reducers: {
15 | auth_login: (state: AuthState, action: PayloadAction) => {
16 | state.loggedIn = action.payload;
17 | },
18 | },
19 | });
20 |
21 | // Action creators are generated for each case reducer function
22 | export const { auth_login } = userAuthSlice.actions;
23 |
24 | export default userAuthSlice.reducer;
25 |
--------------------------------------------------------------------------------
/front/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 0;
3 | margin: 0;
4 | }
--------------------------------------------------------------------------------
/front/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | html,
5 | body {
6 | padding: 0;
7 | margin: 0;
8 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
9 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
10 | }
11 |
12 | a {
13 | color: inherit;
14 | text-decoration: none;
15 | }
16 |
17 | * {
18 | box-sizing: border-box;
19 | }
20 | /* width */
21 | #scroll::-webkit-scrollbar {
22 | width: 1px;
23 | }
24 |
25 | /* Track */
26 | #scroll::-webkit-scrollbar-track {
27 | background: #f1f1f1;
28 | }
29 |
30 | /* Handle */
31 | #scroll::-webkit-scrollbar-thumb {
32 | background: rgb(133, 132, 132);
33 | }
34 |
35 | /* Chrome, Safari, Edge, Opera */
36 | input::-webkit-outer-spin-button,
37 | input::-webkit-inner-spin-button {
38 | -webkit-appearance: none;
39 | margin: 0;
40 | }
41 |
42 | /* Firefox */
43 | input[type=number] {
44 | -moz-appearance: textfield;
45 | }
--------------------------------------------------------------------------------
/front/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [
3 | "./pages/**/*.{js,ts,jsx,tsx}",
4 | "./components/**/*.{js,ts,jsx,tsx}",
5 | ],
6 | theme: {
7 | extend: {
8 | gridTemplateRows: {
9 | "[auto,auto,1fr]": "auto auto 1fr",
10 | },
11 | },
12 | },
13 | plugins: [
14 | require("@tailwindcss/aspect-ratio"),
15 | require("@tailwindcss/forms"),
16 | ],
17 | };
18 |
--------------------------------------------------------------------------------
/front/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/front/util/axios.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import getConfig from "next/config";
3 | const { publicRuntimeConfig } = getConfig();
4 |
5 | const Axios = axios.create({
6 | withCredentials: true,
7 | baseURL: publicRuntimeConfig.backendUrl,
8 | });
9 |
10 | export default Axios;
11 |
--------------------------------------------------------------------------------
/front/util/storage.ts:
--------------------------------------------------------------------------------
1 | import createWebStorage from "redux-persist/lib/storage/createWebStorage";
2 | const createNoopStorage = () => {
3 | return {
4 | getItem(_key: any) {
5 | return Promise.resolve(null);
6 | },
7 | setItem(_key: any, value: any) {
8 | return Promise.resolve(value);
9 | },
10 | removeItem(_key: any) {
11 | return Promise.resolve();
12 | },
13 | };
14 | };
15 | const storage =
16 | typeof window !== "undefined"
17 | ? createWebStorage("local")
18 | : createNoopStorage();
19 |
20 | export default storage;
21 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module gonextjs
2 |
3 | go 1.17
4 |
5 | require (
6 | github.com/bxcodec/faker/v3 v3.7.0
7 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
8 | github.com/gofiber/fiber/v2 v2.37.1
9 | github.com/joho/godotenv v1.4.0
10 | golang.org/x/crypto v0.0.0-20220214200702-86341886e292
11 | gorm.io/driver/postgres v1.3.9
12 | gorm.io/gorm v1.23.9
13 | )
14 |
15 | require (
16 | github.com/andybalholm/brotli v1.0.4 // indirect
17 | github.com/go-playground/locales v0.14.0 // indirect
18 | github.com/go-playground/universal-translator v0.18.0 // indirect
19 | github.com/go-playground/validator/v10 v10.11.1 // indirect
20 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect
21 | github.com/jackc/pgconn v1.12.1 // indirect
22 | github.com/jackc/pgio v1.0.0 // indirect
23 | github.com/jackc/pgpassfile v1.0.0 // indirect
24 | github.com/jackc/pgproto3/v2 v2.3.0 // indirect
25 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
26 | github.com/jackc/pgtype v1.11.0 // indirect
27 | github.com/jackc/pgx/v4 v4.16.1 // indirect
28 | github.com/jinzhu/inflection v1.0.0 // indirect
29 | github.com/jinzhu/now v1.1.4 // indirect
30 | github.com/klauspost/compress v1.15.0 // indirect
31 | github.com/leodido/go-urn v1.2.1 // indirect
32 | github.com/valyala/bytebufferpool v1.0.0 // indirect
33 | github.com/valyala/fasthttp v1.40.0 // indirect
34 | github.com/valyala/tcplisten v1.0.0 // indirect
35 | golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
36 | golang.org/x/text v0.3.7 // indirect
37 | )
38 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2 | github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
3 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
4 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
5 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
6 | github.com/bxcodec/faker/v3 v3.7.0 h1:qWAFFwcyVS0ukF0UoJju1wBLO0cuPQ7JdVBPggM8kNo=
7 | github.com/bxcodec/faker/v3 v3.7.0/go.mod h1:gF31YgnMSMKgkvl+fyEo1xuSMbEuieyqfeslGYFjneM=
8 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
9 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
10 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
11 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
12 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
13 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
14 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
16 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
18 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
19 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
20 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
21 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
22 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
23 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
24 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
25 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
26 | github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
27 | github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
28 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
29 | github.com/gofiber/fiber/v2 v2.37.1 h1:QK2032gjv0ulegpv/qlTEBoXQD3eFFzCHXcNN12UZCs=
30 | github.com/gofiber/fiber/v2 v2.37.1/go.mod h1:j3UslgQeJQP3mNhBxHnLLE8TPqA1Fd/lrl4gD25rRUY=
31 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
32 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
33 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
34 | github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
35 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
36 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
37 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
38 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
39 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
40 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
41 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
42 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
43 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
44 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
45 | github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
46 | github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
47 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
48 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
49 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
50 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
51 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
52 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
53 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
54 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
55 | github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
56 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
57 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
58 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
59 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
60 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
61 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
62 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
63 | github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
64 | github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
65 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
66 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
67 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
68 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
69 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
70 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
71 | github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
72 | github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
73 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
74 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
75 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
76 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
77 | github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
78 | github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
79 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
80 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
81 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
82 | github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
83 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
84 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
85 | github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
86 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
87 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
88 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
89 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
90 | github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
91 | github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
92 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
93 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
94 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
95 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
96 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
97 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
98 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
99 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
100 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
101 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
102 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
103 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
104 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
105 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
106 | github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
107 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
108 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
109 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
110 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
111 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
112 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
113 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
114 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
115 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
116 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
117 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
118 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
119 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
120 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
121 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
122 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
123 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
124 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
125 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
126 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
127 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
128 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
129 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
130 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
131 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
132 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
133 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
134 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
135 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
136 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
137 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
138 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
139 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
140 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
141 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
142 | github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc=
143 | github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
144 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
145 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
146 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
147 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
148 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
149 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
150 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
151 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
152 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
153 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
154 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
155 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
156 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
157 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
158 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
159 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
160 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
161 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
162 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
163 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
164 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
165 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
166 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
167 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
168 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
169 | golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
170 | golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
171 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
172 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
173 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
174 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
175 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
176 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
177 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
178 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
179 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
180 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
181 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
182 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
183 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
184 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
185 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
186 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
187 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
188 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
189 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
190 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
191 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
192 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
193 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
194 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
195 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
196 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
197 | golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
198 | golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
199 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
200 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
201 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
202 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
203 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
204 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
205 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
206 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
207 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
208 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
209 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
210 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
211 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
212 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
213 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
214 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
215 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
216 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
217 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
218 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
219 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
220 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
221 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
222 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
223 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
224 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
225 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
226 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
227 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
228 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
229 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
230 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
231 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
232 | gorm.io/driver/postgres v1.3.9 h1:lWGiVt5CijhQAg0PWB7Od1RNcBw/jS4d2cAScBcSDXg=
233 | gorm.io/driver/postgres v1.3.9/go.mod h1:qw/FeqjxmYqW5dBcYNBsnhQULIApQdk7YuuDPktVi1U=
234 | gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
235 | gorm.io/gorm v1.23.9 h1:NSHG021i+MCznokeXR3udGaNyFyBQJW8MbjrJMVCfGw=
236 | gorm.io/gorm v1.23.9/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
237 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
238 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gofiber/fiber/v2"
5 | "github.com/gofiber/fiber/v2/middleware/cors"
6 | "github.com/joho/godotenv"
7 | "gonextjs/database"
8 | "gonextjs/routes"
9 | "os"
10 | )
11 |
12 | func main() {
13 | errenv := godotenv.Load()
14 | if errenv != nil {
15 | return
16 | }
17 | config := fiber.Config{
18 | Prefork: false,
19 | CaseSensitive: true,
20 | StrictRouting: true,
21 | DisableHeaderNormalizing: true,
22 | ServerHeader: "go",
23 | }
24 |
25 | app := fiber.New(config)
26 | app.Use(cors.New(cors.Config{
27 | Next: nil,
28 | AllowOrigins: "*",
29 | AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH",
30 | AllowHeaders: "",
31 | AllowCredentials: true,
32 | ExposeHeaders: "",
33 | MaxAge: 0,
34 | }))
35 | database.Connect()
36 | database.AutoMigrate()
37 | routes.SetupRoutes(app)
38 | err := app.Listen(":" + os.Getenv("PORT"))
39 | if err != nil {
40 | println(err.Error())
41 | return
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/middlewares/auth.go:
--------------------------------------------------------------------------------
1 | package middlewares
2 |
3 | import (
4 | "strconv"
5 |
6 | "github.com/dgrijalva/jwt-go"
7 | "github.com/gofiber/fiber/v2"
8 | )
9 |
10 | func IsAuthenticated(c *fiber.Ctx) error {
11 | cookie := c.Cookies("jwt")
12 | token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(t *jwt.Token) (interface{}, error) {
13 | return []byte("secret"), nil
14 | })
15 | if err != nil || !token.Valid {
16 | return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
17 | "messsage": "unauthenticated",
18 | })
19 | }
20 | return c.Next()
21 | }
22 |
23 | func GetUserId(c *fiber.Ctx) (uint, error) {
24 | cookie := c.Cookies("jwt")
25 | token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(t *jwt.Token) (interface{}, error) {
26 | return []byte("secret"), nil
27 | })
28 | if err != nil {
29 | return 0, err
30 | }
31 | payload := token.Claims.(*jwt.StandardClaims)
32 | id, _ := strconv.Atoi(payload.Subject)
33 | return uint(id), nil
34 | }
35 |
--------------------------------------------------------------------------------
/models/cart.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type Cart struct {
6 | gorm.Model
7 | UserID uint
8 | StoreID uint
9 | CartItems []CartItems
10 | }
11 |
--------------------------------------------------------------------------------
/models/cartitems.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type CartItems struct {
6 | gorm.Model
7 | CartID uint
8 | ProductID uint
9 | }
10 |
--------------------------------------------------------------------------------
/models/categories.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "gorm.io/gorm"
5 | )
6 |
7 | type Categories struct {
8 | gorm.Model
9 | Description string `json:"description"`
10 | Product []Product
11 | }
12 |
--------------------------------------------------------------------------------
/models/coupons.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "gorm.io/gorm"
5 | "time"
6 | )
7 |
8 | type Coupons struct {
9 | gorm.Model
10 | Code string `json:"code"`
11 | ExpireTime time.Time `json:"expireTime"`
12 | Enabled bool `json:"enabled"`
13 | }
14 |
--------------------------------------------------------------------------------
/models/currency.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type Currency struct {
6 | gorm.Model
7 | Title string `json:"title"`
8 | Code string `json:"code"`
9 | }
10 |
--------------------------------------------------------------------------------
/models/order.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type Order struct {
6 | gorm.Model
7 | OrderId string `json:"orderId"`
8 | UserID uint
9 | StoreID uint
10 | status string
11 | Store Store `gorm:"foreignKey:StoreID"`
12 | User User `gorm:"foreignKey:UserID"`
13 | }
14 |
--------------------------------------------------------------------------------
/models/product.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type Product struct {
6 | gorm.Model
7 | Title string `json:"title"`
8 | Description string `json:"description"`
9 | Weight float32 `json:"weight"`
10 | Length float32 `json:"length"`
11 | Width float32 `json:"width"`
12 | Height float32 `json:"height"`
13 | Price float32 `json:"price"`
14 | AdditionalPrice float32 `json:"additionalPrice"`
15 | Image string `json:"image"`
16 | QTY int `json:"QTY"`
17 | UserID uint
18 | StoreID uint
19 | CategoriesID uint
20 | CurrencyID uint
21 | Currency Currency `gorm:"foreignKey:CurrencyID"`
22 | Categories Categories `gorm:"foreignKey:CategoriesID"`
23 | Store Store `gorm:"foreignKey:StoreID"`
24 | User User `gorm:"foreignKey:UserID"`
25 | }
26 |
--------------------------------------------------------------------------------
/models/product_sold.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type ProductsSold struct {
6 | gorm.Model
7 | QTY int `json:"QTY"`
8 | Price int `json:"price"`
9 | ProductId int `json:"productId"`
10 | OrderId int `json:"orderId"`
11 | Order Order `gorm:"foreignKey:OrderId"`
12 | Product Product `gorm:"foreignKey:ProductId"`
13 | }
14 |
--------------------------------------------------------------------------------
/models/reviews.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type Reviews struct {
6 | gorm.Model
7 | Ratting int `json:"ratting"`
8 | Body string `json:"body"`
9 | UserID uint
10 | ProductID uint
11 | }
12 |
--------------------------------------------------------------------------------
/models/store.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type Store struct {
6 | gorm.Model
7 | Name string `json:"name"`
8 | Location string `json:"location"`
9 | Product []Product
10 | Cart []Cart
11 | Order []Order
12 | }
13 |
--------------------------------------------------------------------------------
/models/user.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "golang.org/x/crypto/bcrypt"
5 | "gorm.io/gorm"
6 | )
7 |
8 | type User struct {
9 | gorm.Model
10 | FirstName string `json:"firstname"`
11 | LastName string `json:"lastname"`
12 | Email string `json:"email" gorm:"not null;unique" validate:"email"`
13 | Password []byte `json:"-"`
14 | TypeId uint `json:"type_id"`
15 | UserTypes UserTypes `gorm:"foreignKey:TypeId"`
16 | }
17 |
18 | type NewUser struct {
19 | FirstName string `json:"firstname" validate:"required"`
20 | LastName string `json:"lastname" validate:"required"`
21 | Email string `json:"email" gorm:"not null;unique" validate:"email"`
22 | Password string `json:"password" validate:"required"`
23 | PasswordConfirm string `json:"password_confirm" validate:"required"`
24 | }
25 |
26 | type UpdateUser struct {
27 | FirstName string `json:"firstname"`
28 | LastName string `json:"lastname"`
29 | Email string `json:"email" validate:"email"`
30 | }
31 |
32 | type UserPassword struct {
33 | CurrentPassword string `json:"current_password" validate:"required"`
34 | Password string `json:"password" validate:"required,gte=7"`
35 | PasswordConfirm string `json:"password_confirm" validate:"required,gte=7"`
36 | }
37 |
38 | type Login struct {
39 | Email string `json:"email" validate:"email"`
40 | Password string `json:"password" validate:"required,gte=7"`
41 | }
42 |
43 | func (user *User) SetPassword(password string) {
44 | hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), 12)
45 | user.Password = hashedPassword
46 | }
47 |
48 | func (user *User) ComparePasswords(password string) error {
49 | return bcrypt.CompareHashAndPassword(user.Password, []byte(password))
50 | }
51 |
--------------------------------------------------------------------------------
/models/user_types.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "gorm.io/gorm"
4 |
5 | type UserTypes struct {
6 | gorm.Model
7 | UserType string `json:"user_type"`
8 | }
9 |
--------------------------------------------------------------------------------
/routes/routes.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | "gonextjs/controllers"
5 | "gonextjs/middlewares"
6 |
7 | "github.com/gofiber/fiber/v2"
8 | )
9 |
10 | func SetupRoutes(app *fiber.App) {
11 | api := app.Group("api")
12 | admin := api.Group("admin")
13 | api.Get("/status", func(ctx *fiber.Ctx) error {
14 | return ctx.JSON(fiber.Map{
15 | "message": "success",
16 | //"message": "Server In Maintenance Mode, Come Back After 2Hr.🙃",
17 | })
18 | })
19 | api.Post("/login", controllers.Login)
20 | api.Post("/register", controllers.Register)
21 | adminAuthenticated := admin.Use(middlewares.IsAuthenticated)
22 | adminAuthenticated.Get("/user", controllers.User)
23 | adminAuthenticated.Get("/logout", controllers.Logout)
24 | adminAuthenticated.Put("/users/info", controllers.UpdateInfo)
25 | adminAuthenticated.Put("/users/password", controllers.UpdatePassword)
26 |
27 | /* Product */
28 | // public routes
29 | api.Get("/products", controllers.GetAllProducts)
30 | api.Get("/products/:id", controllers.GetProduct)
31 | // Admin routes
32 | adminAuthenticated.Post("/product", controllers.AddProducts)
33 | adminAuthenticated.Put("/products/:id", controllers.UpdateProduct)
34 | adminAuthenticated.Delete("/products/:id", controllers.DeleteProduct)
35 |
36 | /* Currency */
37 | adminAuthenticated.Post("/currency", controllers.AddCurrency)
38 | adminAuthenticated.Get("/currency", controllers.GetAllCurrencies)
39 |
40 | /* Store */
41 | adminAuthenticated.Post("/store", controllers.AddStore)
42 | adminAuthenticated.Get("/store", controllers.GetAllStores)
43 |
44 | /* Category */
45 | adminAuthenticated.Post("/category", controllers.AddCategory)
46 | adminAuthenticated.Get("/category", controllers.GetAllCategories)
47 | }
48 |
--------------------------------------------------------------------------------
/runner.conf:
--------------------------------------------------------------------------------
1 | root: .
2 | tmp_path: ./tmp
3 | build_name: runner-build
4 | build_log: runner-build-errors.log
5 | valid_ext: .go, .tpl, .tmpl, .html
6 | no_rebuild_ext: .tpl, .tmpl, .html
7 | ignored: assets, tmp, front
8 | build_delay: 0
9 | colors: 1
10 | log_color_main: cyan
11 | log_color_build: yellow
12 | log_color_runner: green
13 | log_color_watcher: magenta
14 | log_color_app:
--------------------------------------------------------------------------------
/util/helper.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "strconv"
4 |
5 | func ParseFloat32(s string) float32 {
6 | result, _ := strconv.ParseFloat(s, 32)
7 | return float32(result)
8 | }
9 |
10 | func ParseFloat64(s string) float64 {
11 | result, _ := strconv.ParseFloat(s, 64)
12 | return result
13 | }
14 |
15 | func ParseInt(s string) int {
16 | result, _ := strconv.Atoi(s)
17 | return result
18 | }
19 |
--------------------------------------------------------------------------------