Compare commits
37 Commits
version-bu
...
add-vin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9d24bc7ef | ||
|
|
d3ce6920ad | ||
|
|
afdfa31148 | ||
|
|
2d24c4b9e6 | ||
|
|
84cba2c7f2 | ||
|
|
1432499a90 | ||
|
|
d0704c8c6a | ||
|
|
b86795bcb6 | ||
|
|
1ccdce9ee3 | ||
|
|
5cfaf8c933 | ||
|
|
987f035198 | ||
|
|
ab94997dd6 | ||
|
|
0b715ef840 | ||
|
|
c00c6bc776 | ||
|
|
a5d4dface8 | ||
|
|
7cb9a43dfe | ||
|
|
05bb22fe4e | ||
|
|
69352af906 | ||
|
|
7a8916c9cd | ||
|
|
e471e80617 | ||
|
|
1ee032b664 | ||
|
|
cea2566e2a | ||
|
|
dcb58bbbdb | ||
|
|
24105dbaaf | ||
|
|
e3846634b5 | ||
|
|
fd52c23636 | ||
|
|
43d1ca0c66 | ||
|
|
fb742f19a7 | ||
|
|
20a1421576 | ||
|
|
8410674841 | ||
|
|
74e52c3e87 | ||
|
|
1857bb0518 | ||
|
|
a729b5eb12 | ||
|
|
d9a99d432c | ||
|
|
acba47fede | ||
|
|
04f45fe385 | ||
|
|
fca2c3e7fa |
45
.github/workflows/hub.yml
vendored
45
.github/workflows/hub.yml
vendored
@@ -1,45 +1,46 @@
|
||||
name: ci
|
||||
name: Build docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: master
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
multi:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
- name: Available platforms
|
||||
run: echo ${{ steps.qemu.outputs.platforms }}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Set up build cache
|
||||
- name: Set up build cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
- name: Parse the git tag
|
||||
id: get_tag
|
||||
run: echo ::set-output name=TAG::$(echo $GITHUB_REF | cut -d / -f 3)
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Login to GitHub
|
||||
- name: Login to GitHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
-
|
||||
name: Build and push
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
@@ -48,10 +49,10 @@ jobs:
|
||||
#platforms: linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
# cache-from: type=local,src=/tmp/.buildx-cache
|
||||
# cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
tags: |
|
||||
akhilrex/hammond:latest
|
||||
akhilrex/hammond:1.0.0
|
||||
akhilrex/hammond:${{ steps.get_tag.outputs.TAG }}
|
||||
ghcr.io/akhilrex/hammond:latest
|
||||
ghcr.io/akhilrex/hammond:1.0.0
|
||||
ghcr.io/akhilrex/hammond:${{ steps.get_tag.outputs.TAG }}
|
||||
|
||||
16
.github/workflows/test-go.yml
vendored
Normal file
16
.github/workflows/test-go.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
on: [push, pull_request]
|
||||
name: Test server
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.17.x, 1.18.x]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- uses: actions/checkout@v3
|
||||
- run: go test ./...
|
||||
working-directory: server
|
||||
22
.github/workflows/test-npm.yml
vendored
Normal file
22
.github/workflows/test-npm.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Test UI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
working-directory: ui
|
||||
- run: npm run build --if-present
|
||||
working-directory: ui
|
||||
- run: npm test
|
||||
working-directory: ui
|
||||
@@ -9,7 +9,7 @@ RUN go mod download
|
||||
COPY ./server .
|
||||
RUN go build -o ./app ./main.go
|
||||
|
||||
FROM node:latest as build-stage
|
||||
FROM node:14 as build-stage
|
||||
WORKDIR /app
|
||||
COPY ./ui/package*.json ./
|
||||
RUN npm install
|
||||
@@ -36,4 +36,4 @@ COPY --from=builder /api/app .
|
||||
#COPY dist ./dist
|
||||
COPY --from=build-stage /app/dist ./dist
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["./app"]
|
||||
ENTRYPOINT ["./app"]
|
||||
|
||||
28
README.md
28
README.md
@@ -8,7 +8,7 @@
|
||||
</a> -->
|
||||
|
||||
<h1 align="center" style="margin-bottom:0">Hammond</h1>
|
||||
<p align="center">Current Version - 2021.09.20</p>
|
||||
<p align="center">Current Version - 2022.07.06</p>
|
||||
|
||||
<p align="center">
|
||||
A self-hosted vehicle expense tracking system with support for multiple users.
|
||||
@@ -35,6 +35,7 @@
|
||||
- [Built With](#built-with)
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
- [Roadmap](#roadmap)
|
||||
- [Contact](#contact)
|
||||
@@ -157,6 +158,31 @@ Once done you will be taken to the login page.
|
||||
|
||||
Go through the settings page once and change relevant settings before you start adding vehicles and expenses.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Dev Setup
|
||||
|
||||
If you want to contribute to the project you need to set it up
|
||||
for development first.
|
||||
|
||||
Fork and clone the project. Once you have it on your own machine,
|
||||
open up a terminal and navigate to the `server/` directory.
|
||||
|
||||
In the `server/` directory run the command `go run main.go`.
|
||||
After some initial
|
||||
setup, the server should be listening on at port `3000`.
|
||||
|
||||
Next, open a new terminal. Navigate to the `ui/` directory and run `npm install`.
|
||||
This will install all the dependencies for the frontend.
|
||||
After the command is done running, run `npm run dev`. After some output, the
|
||||
frontend should be accessible at `http://localhost:8080`.
|
||||
|
||||
If you are sent straight to the login screen, try closing the page and opening
|
||||
it again. You should be greeted with a setup wizard the first time you run the
|
||||
project.
|
||||
|
||||
Now, simply follow the instructions in order to set up your fresh install.
|
||||
|
||||
## License
|
||||
|
||||
Distributed under the GPL-3.0 License. See `LICENSE` for more information.
|
||||
|
||||
@@ -4,7 +4,7 @@ services:
|
||||
image: akhilrex/hammond
|
||||
container_name: hammond
|
||||
environment:
|
||||
- JWT_SECRET = somethingverystrong
|
||||
- JWT_SECRET=somethingverystrong
|
||||
volumes:
|
||||
- /path/to/config:/config
|
||||
- /path/to/data:/assets
|
||||
|
||||
4
server/.gitignore
vendored
4
server/.gitignore
vendored
@@ -12,6 +12,10 @@
|
||||
*.out
|
||||
*.db
|
||||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
__debug_bin
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
assets/*
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/akhilrex/hammond/common"
|
||||
"github.com/akhilrex/hammond/db"
|
||||
@@ -91,20 +92,20 @@ func userLogin(c *gin.Context) {
|
||||
c.JSON(http.StatusUnprocessableEntity, common.NewValidatorError(err))
|
||||
return
|
||||
}
|
||||
user, err := db.FindOneUser(&db.User{Email: loginRequest.Email})
|
||||
user, err := db.FindOneUser(&db.User{Email: strings.ToLower(loginRequest.Email)})
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("Not Registered email or invalid password")))
|
||||
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("not Registered email or invalid password")))
|
||||
return
|
||||
}
|
||||
|
||||
if user.CheckPassword(loginRequest.Password) != nil {
|
||||
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("Not Registered email or invalid password")))
|
||||
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("not Registered email or invalid password")))
|
||||
return
|
||||
}
|
||||
|
||||
if user.IsDisabled {
|
||||
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("Your user has been disabled by the admin. Please contact them to get it re-enabled.")))
|
||||
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("your user has been disabled by the admin. Please contact them to get it re-enabled")))
|
||||
return
|
||||
}
|
||||
UpdateContextUserModel(c, user.ID)
|
||||
@@ -170,16 +171,16 @@ func changePassword(c *gin.Context) {
|
||||
user, err := service.GetUserById(c.GetString("userId"))
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("Not Registered email or invalid password")))
|
||||
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("not Registered email or invalid password")))
|
||||
return
|
||||
}
|
||||
|
||||
if user.CheckPassword(request.OldPassword) != nil {
|
||||
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("Incorrect old password")))
|
||||
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("incorrect old password")))
|
||||
return
|
||||
}
|
||||
|
||||
user.SetPassword(request.NewPassword)
|
||||
success, err := service.UpdatePassword(user.ID, request.NewPassword)
|
||||
success, _ := service.UpdatePassword(user.ID, request.NewPassword)
|
||||
c.JSON(http.StatusOK, success)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ func stripBearerPrefixFromTokenString(tok string) (string, error) {
|
||||
// Extract token from Authorization header
|
||||
// Uses PostExtractionFilter to strip "TOKEN " prefix from header
|
||||
var AuthorizationHeaderExtractor = &request.PostExtractionFilter{
|
||||
request.HeaderExtractor{"Authorization"},
|
||||
stripBearerPrefixFromTokenString,
|
||||
Extractor: request.HeaderExtractor{"Authorization"},
|
||||
Filter: stripBearerPrefixFromTokenString,
|
||||
}
|
||||
|
||||
// Extractor for OAuth2 access tokens. Looks in 'Authorization'
|
||||
|
||||
@@ -51,7 +51,7 @@ func migrate(c *gin.Context) {
|
||||
canMigrate, _, _ := db.CanMigrate(request.Url)
|
||||
|
||||
if !canMigrate {
|
||||
c.JSON(http.StatusBadRequest, fmt.Errorf("cannot migrate database. please check connection string."))
|
||||
c.JSON(http.StatusBadRequest, fmt.Errorf("cannot migrate database. please check connection string"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ func deleteVehicle(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
if !canDelete {
|
||||
c.JSON(http.StatusUnprocessableEntity, common.NewError("shareVehicle", errors.New("You are not allowed to delete this vehicle.")))
|
||||
c.JSON(http.StatusUnprocessableEntity, common.NewError("shareVehicle", errors.New("you are not allowed to delete this vehicle")))
|
||||
return
|
||||
}
|
||||
err = service.DeleteVehicle(searchByIdQuery.Id)
|
||||
|
||||
@@ -60,6 +60,7 @@ type Vehicle struct {
|
||||
Base
|
||||
Nickname string `json:"nickname"`
|
||||
Registration string `json:"registration"`
|
||||
VIN string `json:"vin"`
|
||||
Make string `json:"make"`
|
||||
Model string `json:"model"`
|
||||
YearOfManufacture int `json:"yearOfManufacture"`
|
||||
@@ -195,3 +196,50 @@ type VehicleAttachment struct {
|
||||
VehicleID string `gorm:"primaryKey" json:"vehicleId"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
type VehicleAlert struct {
|
||||
Base
|
||||
VehicleID string `json:"vehicleId"`
|
||||
Vehicle Vehicle `json:"-"`
|
||||
UserID string `json:"userId"`
|
||||
User User `json:"user"`
|
||||
Title string `json:"title"`
|
||||
Comments string `json:"comments"`
|
||||
StartDate time.Time `json:"date"`
|
||||
StartOdoReading int `json:"startOdoReading"`
|
||||
DistanceUnit DistanceUnit `json:"distanceUnit"`
|
||||
AlertFrequency AlertFrequency `json:"alertFrequency"`
|
||||
OdoFrequency int `json:"odoFrequency"`
|
||||
DayFrequency int `json:"dayFrequency"`
|
||||
AlertAllUsers bool `json:"alertAllUsers"`
|
||||
IsActive bool `json:"isActive"`
|
||||
EndDate *time.Time `json:"endDate"`
|
||||
AlertType AlertType `json:"alertType"`
|
||||
}
|
||||
type AlertOccurance struct {
|
||||
Base
|
||||
VehicleID string `json:"vehicleId"`
|
||||
Vehicle Vehicle `json:"-"`
|
||||
VehicleAlertID string `json:"vehicleAlertId"`
|
||||
VehicleAlert VehicleAlert `json:"-"`
|
||||
UserID string `json:"userId"`
|
||||
User User `json:"-"`
|
||||
OdoReading int `json:"odoReading"`
|
||||
Date *time.Time `json:"date"`
|
||||
ProcessDate *time.Time `json:"processDate"`
|
||||
AlertProcessType AlertType `json:"alertProcessType"`
|
||||
CompleteDate *time.Time `json:"completeDate"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Base
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
UserID string `json:"userId"`
|
||||
VehicleID string `json:"vehicleId"`
|
||||
User User `json:"-"`
|
||||
Date time.Time `json:"date"`
|
||||
ReadDate *time.Time `json:"readDate"`
|
||||
ParentID string `json:"parentId"`
|
||||
ParentType string `json:"parentType"`
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func UnshareVehicle(vehicleId, userId string) error {
|
||||
return nil
|
||||
}
|
||||
if mapping.IsOwner {
|
||||
return fmt.Errorf("Cannot unshare owner")
|
||||
return fmt.Errorf("cannot unshare owner")
|
||||
}
|
||||
result := DB.Where("id=?", mapping.ID).Delete(&UserVehicle{})
|
||||
return result.Error
|
||||
@@ -160,6 +160,11 @@ func GetFillupsByVehicleId(id string) (*[]Fillup, error) {
|
||||
result := DB.Preload(clause.Associations).Order("date desc").Find(&obj, &Fillup{VehicleID: id})
|
||||
return &obj, result.Error
|
||||
}
|
||||
func GetLatestFillupsByVehicleId(id string) (*Fillup, error) {
|
||||
var obj Fillup
|
||||
result := DB.Preload(clause.Associations).Order("date desc").First(&obj, &Fillup{VehicleID: id})
|
||||
return &obj, result.Error
|
||||
}
|
||||
func GetFillupsByVehicleIdSince(id string, since time.Time) (*[]Fillup, error) {
|
||||
var obj []Fillup
|
||||
result := DB.Where("date >= ? AND vehicle_id = ?", since, id).Preload(clause.Associations).Order("date desc").Find(&obj)
|
||||
@@ -190,6 +195,11 @@ func GetExpensesByVehicleId(id string) (*[]Expense, error) {
|
||||
result := DB.Preload(clause.Associations).Order("date desc").Find(&obj, &Expense{VehicleID: id})
|
||||
return &obj, result.Error
|
||||
}
|
||||
func GetLatestExpenseByVehicleId(id string) (*Expense, error) {
|
||||
var obj Expense
|
||||
result := DB.Preload(clause.Associations).Order("date desc").First(&obj, &Expense{VehicleID: id})
|
||||
return &obj, result.Error
|
||||
}
|
||||
func GetExpenseById(id string) (*Expense, error) {
|
||||
var obj Expense
|
||||
result := DB.Preload(clause.Associations).First(&obj, "id=?", id)
|
||||
@@ -271,6 +281,29 @@ func GetVehicleAttachments(vehicleId string) (*[]Attachment, error) {
|
||||
}
|
||||
return &attachments, nil
|
||||
}
|
||||
func GeAlertById(id string) (*VehicleAlert, error) {
|
||||
var alert VehicleAlert
|
||||
result := DB.Preload(clause.Associations).First(&alert, "id=?", id)
|
||||
return &alert, result.Error
|
||||
}
|
||||
func GetAlertOccurenceByAlertId(id string) (*[]AlertOccurance, error) {
|
||||
var alertOccurance []AlertOccurance
|
||||
result := DB.Preload(clause.Associations).Order("created_at desc").Find(&alertOccurance, "vehicle_alert_id=?", id)
|
||||
return &alertOccurance, result.Error
|
||||
}
|
||||
|
||||
func GetUnprocessedAlertOccurances() (*[]AlertOccurance, error) {
|
||||
var alertOccurance []AlertOccurance
|
||||
result := DB.Preload(clause.Associations).Order("created_at desc").Find(&alertOccurance, "process_date is NULL")
|
||||
return &alertOccurance, result.Error
|
||||
}
|
||||
func MarkAlertOccuranceAsProcessed(id string, alertProcessType AlertType, date time.Time) error {
|
||||
tx := DB.Debug().Model(&AlertOccurance{}).Where("id= ?", id).
|
||||
Update("alert_process_type", alertProcessType).
|
||||
Update("process_date", date)
|
||||
return tx.Error
|
||||
|
||||
}
|
||||
|
||||
func UpdateSettings(setting *Setting) error {
|
||||
tx := DB.Save(&setting)
|
||||
@@ -332,8 +365,7 @@ func UnlockMissedJobs() {
|
||||
if (job.Date == time.Time{}) {
|
||||
continue
|
||||
}
|
||||
var duration time.Duration
|
||||
duration = time.Duration(job.Duration)
|
||||
var duration = time.Duration(job.Duration)
|
||||
d := job.Date.Add(time.Minute * duration)
|
||||
if d.Before(time.Now()) {
|
||||
fmt.Println(job.Name + " is unlocked")
|
||||
|
||||
@@ -36,6 +36,21 @@ const (
|
||||
USER
|
||||
)
|
||||
|
||||
type AlertFrequency int
|
||||
|
||||
const (
|
||||
ONETIME AlertFrequency = iota
|
||||
RECURRING
|
||||
)
|
||||
|
||||
type AlertType int
|
||||
|
||||
const (
|
||||
DISTANCE AlertType = iota
|
||||
TIME
|
||||
BOTH
|
||||
)
|
||||
|
||||
type EnumDetail struct {
|
||||
Short string `json:"short"`
|
||||
Long string `json:"long"`
|
||||
|
||||
@@ -18,6 +18,15 @@ var migrations = []localMigration{
|
||||
Name: "2021_06_24_04_42_SetUserDisabledFalse",
|
||||
Query: "update users set is_disabled=0",
|
||||
},
|
||||
{
|
||||
Name: "2021_02_07_00_09_LowerCaseEmails",
|
||||
Query: "update users set email=lower(email)",
|
||||
|
||||
},
|
||||
{
|
||||
Name: "2022_03_08_13_16_AddVIN",
|
||||
Query: "ALTER TABLE vehicles ADD COLUMN vin text",
|
||||
},
|
||||
}
|
||||
|
||||
func RunMigrations() {
|
||||
|
||||
@@ -5,28 +5,17 @@ go 1.16
|
||||
require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/gin-contrib/location v0.0.2
|
||||
github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/go-playground/validator/v10 v10.10.0
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 // indirect
|
||||
github.com/gin-gonic/gin v1.7.1
|
||||
github.com/go-playground/validator/v10 v10.4.1
|
||||
github.com/jasonlvhit/gocron v0.0.1
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leekchan/accounting v1.0.0
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/leekchan/accounting v1.0.0 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/ugorji/go v1.2.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gorm.io/driver/mysql v1.3.2
|
||||
gorm.io/driver/sqlite v1.3.1
|
||||
gorm.io/gorm v1.23.2
|
||||
gorm.io/driver/mysql v1.0.5
|
||||
gorm.io/driver/sqlite v1.1.4
|
||||
gorm.io/gorm v1.21.3
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -16,35 +15,21 @@ github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19/go.mod h1:iqneQ2
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8=
|
||||
github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jasonlvhit/gocron v0.0.1 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU=
|
||||
@@ -53,95 +38,55 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leekchan/accounting v1.0.0 h1:+Wd7dJ//dFPa28rc1hjyy+qzCbXPMR91Fb6F1VGTQHg=
|
||||
github.com/leekchan/accounting v1.0.0/go.mod h1:3timm6YPhY3YDaGxl0q3eaflX0eoSx3FXn7ckHe4tO0=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -151,53 +96,24 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o=
|
||||
gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
|
||||
gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
|
||||
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
|
||||
gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
|
||||
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
|
||||
gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk=
|
||||
gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
|
||||
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.21.3 h1:qDFi55ZOsjZTwk5eN+uhAmHi8GysJ/qCTichM/yO7ME=
|
||||
gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.2 h1:xmq9QRMWL8HTJyhAUBXy8FqIIQCYESeKfJL4DoGKiWQ=
|
||||
gorm.io/gorm v1.23.2/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
|
||||
21
server/models/alert.go
Normal file
21
server/models/alert.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/akhilrex/hammond/db"
|
||||
)
|
||||
|
||||
type CreateAlertModel struct {
|
||||
Comments string `json:"comments"`
|
||||
Title string `json:"title"`
|
||||
StartDate time.Time `json:"date"`
|
||||
StartOdoReading int `json:"startOdoReading"`
|
||||
DistanceUnit *db.DistanceUnit `json:"distanceUnit"`
|
||||
AlertFrequency *db.AlertFrequency `json:"alertFrequency"`
|
||||
OdoFrequency int `json:"odoFrequency"`
|
||||
DayFrequency int `json:"dayFrequency"`
|
||||
AlertAllUsers bool `json:"alertAllUsers"`
|
||||
IsActive bool `json:"isActive"`
|
||||
AlertType *db.AlertType `json:"alertType"`
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type SubItemQuery struct {
|
||||
type CreateVehicleRequest struct {
|
||||
Nickname string `form:"nickname" json:"nickname" binding:"required"`
|
||||
Registration string `form:"registration" json:"registration" binding:"required"`
|
||||
VIN string `form:"vin" json:"vin"`
|
||||
Make string `form:"make" json:"make" binding:"required"`
|
||||
Model string `form:"model" json:"model" binding:"required"`
|
||||
YearOfManufacture int `form:"yearOfManufacture" json:"yearOfManufacture"`
|
||||
|
||||
172
server/service/alertSevice.go
Normal file
172
server/service/alertSevice.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/akhilrex/hammond/db"
|
||||
"github.com/akhilrex/hammond/models"
|
||||
)
|
||||
|
||||
func CreateAlert(model models.CreateAlertModel, vehicleId, userId string) (*db.VehicleAlert, error) {
|
||||
alert := db.VehicleAlert{
|
||||
VehicleID: vehicleId,
|
||||
UserID: userId,
|
||||
Title: model.Title,
|
||||
Comments: model.Comments,
|
||||
StartDate: model.StartDate,
|
||||
StartOdoReading: model.StartOdoReading,
|
||||
DistanceUnit: *model.DistanceUnit,
|
||||
AlertFrequency: *model.AlertFrequency,
|
||||
OdoFrequency: model.OdoFrequency,
|
||||
DayFrequency: model.DayFrequency,
|
||||
AlertAllUsers: model.AlertAllUsers,
|
||||
IsActive: model.IsActive,
|
||||
AlertType: *model.AlertType,
|
||||
}
|
||||
tx := db.DB.Create(&alert)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
go CreateAlertInstance(alert.ID)
|
||||
return &alert, nil
|
||||
}
|
||||
|
||||
func CreateAlertInstance(alertId string) error {
|
||||
alert, err := db.GeAlertById(alertId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existingOccurence, err := db.GetAlertOccurenceByAlertId(alertId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var lastOccurance db.AlertOccurance
|
||||
useOccurance := false
|
||||
|
||||
if len(*existingOccurence) > 0 {
|
||||
lastOccurance = (*existingOccurence)[0]
|
||||
useOccurance = true
|
||||
if alert.AlertFrequency == db.ONETIME {
|
||||
return errors.New("Only single occurance is possible for this kind of alert")
|
||||
}
|
||||
}
|
||||
users := []string{alert.UserID}
|
||||
if alert.AlertAllUsers {
|
||||
allUsers, err := db.GetVehicleUsers(alert.VehicleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
users = make([]string, len(*allUsers))
|
||||
for i, user := range *allUsers {
|
||||
users[i] = user.UserID
|
||||
}
|
||||
}
|
||||
|
||||
for _, userId := range users {
|
||||
model := db.AlertOccurance{
|
||||
VehicleID: alert.VehicleID,
|
||||
UserID: userId,
|
||||
VehicleAlertID: alertId,
|
||||
}
|
||||
|
||||
if alert.AlertType == db.DISTANCE || alert.AlertType == db.BOTH {
|
||||
model.OdoReading = alert.StartOdoReading + alert.OdoFrequency
|
||||
if useOccurance {
|
||||
model.OdoReading = lastOccurance.OdoReading + alert.OdoFrequency
|
||||
}
|
||||
}
|
||||
if alert.AlertType == db.TIME || alert.AlertType == db.BOTH {
|
||||
date := alert.StartDate.Add(time.Duration(alert.DayFrequency) * 24 * time.Hour)
|
||||
if useOccurance {
|
||||
date = lastOccurance.Date.Add(time.Duration(alert.DayFrequency) * 24 * time.Hour)
|
||||
}
|
||||
model.Date = &date
|
||||
}
|
||||
tx := db.DB.Create(&model)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func ProcessAlertOccurance(occurance db.AlertOccurance, today time.Time) error {
|
||||
if occurance.ProcessDate != nil {
|
||||
return errors.New("Alert occurence already processed")
|
||||
}
|
||||
alert := occurance.VehicleAlert
|
||||
if !alert.IsActive {
|
||||
return errors.New("Alert is not active")
|
||||
}
|
||||
notification := db.Notification{
|
||||
Title: alert.Title,
|
||||
Content: alert.Comments,
|
||||
UserID: occurance.UserID,
|
||||
VehicleID: occurance.VehicleID,
|
||||
Date: today,
|
||||
ParentID: occurance.ID,
|
||||
ParentType: "AlertOccurance",
|
||||
}
|
||||
var alertProcessType db.AlertType
|
||||
if alert.AlertType == db.DISTANCE || alert.AlertType == db.BOTH {
|
||||
odoReading, err := GetLatestOdoReadingForVehicle(occurance.VehicleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if odoReading >= occurance.OdoReading {
|
||||
alertProcessType = db.DISTANCE
|
||||
}
|
||||
}
|
||||
if alert.AlertType == db.TIME || alert.AlertType == db.BOTH {
|
||||
if occurance.Date.Before(today) {
|
||||
alertProcessType = db.TIME
|
||||
}
|
||||
}
|
||||
|
||||
db.DB.Create(¬ification)
|
||||
return db.MarkAlertOccuranceAsProcessed(occurance.ID, alertProcessType, today)
|
||||
|
||||
}
|
||||
|
||||
func FindAlertOccurancesToProcess(today time.Time) ([]db.AlertOccurance, error) {
|
||||
occurances, err := db.GetUnprocessedAlertOccurances()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(*occurances) == 0 {
|
||||
return make([]db.AlertOccurance, 0), nil
|
||||
}
|
||||
|
||||
var toReturn []db.AlertOccurance
|
||||
|
||||
for _, occurance := range *occurances {
|
||||
alert := occurance.VehicleAlert
|
||||
if !alert.IsActive {
|
||||
continue
|
||||
}
|
||||
if alert.AlertType == db.DISTANCE || alert.AlertType == db.BOTH {
|
||||
odoReading, err := GetLatestOdoReadingForVehicle(occurance.VehicleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if odoReading >= occurance.OdoReading {
|
||||
toReturn = append(toReturn, occurance)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if alert.AlertType == db.TIME || alert.AlertType == db.BOTH {
|
||||
if occurance.Date.Before(today) {
|
||||
toReturn = append(toReturn, occurance)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
func MarkAlertOccuranceAsCompleted() {
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -126,14 +125,14 @@ func CreateBackup() (string, error) {
|
||||
tarballFilePath := path.Join(folder, backupFileName)
|
||||
file, err := os.Create(tarballFilePath)
|
||||
if err != nil {
|
||||
return "", errors.New(fmt.Sprintf("Could not create tarball file '%s', got error '%s'", tarballFilePath, err.Error()))
|
||||
return "", fmt.Errorf("could not create tarball file '%s', got error '%s'", tarballFilePath, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
dbPath := path.Join(configPath, "hammond.db")
|
||||
_, err = os.Stat(dbPath)
|
||||
if err != nil {
|
||||
return "", errors.New(fmt.Sprintf("Could not find db file '%s', got error '%s'", dbPath, err.Error()))
|
||||
return "", fmt.Errorf("could not find db file '%s', got error '%s'", dbPath, err.Error())
|
||||
}
|
||||
gzipWriter := gzip.NewWriter(file)
|
||||
defer gzipWriter.Close()
|
||||
@@ -151,13 +150,13 @@ func CreateBackup() (string, error) {
|
||||
func addFileToTarWriter(filePath string, tarWriter *tar.Writer) error {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not open file '%s', got error '%s'", filePath, err.Error()))
|
||||
return fmt.Errorf("could not open file '%s', got error '%s'", filePath, err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not get stat for file '%s', got error '%s'", filePath, err.Error()))
|
||||
return fmt.Errorf("could not get stat for file '%s', got error '%s'", filePath, err.Error())
|
||||
}
|
||||
|
||||
header := &tar.Header{
|
||||
@@ -169,12 +168,12 @@ func addFileToTarWriter(filePath string, tarWriter *tar.Writer) error {
|
||||
|
||||
err = tarWriter.WriteHeader(header)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not write header for file '%s', got error '%s'", filePath, err.Error()))
|
||||
return fmt.Errorf("could not write header for file '%s', got error '%s'", filePath, err.Error())
|
||||
}
|
||||
|
||||
_, err = io.Copy(tarWriter, file)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("Could not copy the file '%s' data to the tarball, got error '%s'", filePath, err.Error()))
|
||||
return fmt.Errorf("could not copy the file '%s' data to the tarball, got error '%s'", filePath, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/akhilrex/hammond/db"
|
||||
@@ -15,6 +16,9 @@ func GetMileageByVehicleId(vehicleId string, since time.Time) (mileage []models.
|
||||
|
||||
fillups := make([]db.Fillup, len(*data))
|
||||
copy(fillups, *data)
|
||||
sort.Slice(fillups, func(i, j int) bool {
|
||||
return fillups[i].OdoReading > fillups[j].OdoReading
|
||||
})
|
||||
|
||||
var mileages []models.MileageModel
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/akhilrex/hammond/db"
|
||||
"github.com/akhilrex/hammond/models"
|
||||
)
|
||||
@@ -8,7 +10,7 @@ import (
|
||||
func CreateUser(userModel *models.RegisterRequest, role db.Role) error {
|
||||
setting := db.GetOrCreateSetting()
|
||||
toCreate := db.User{
|
||||
Email: userModel.Email,
|
||||
Email: strings.ToLower(userModel.Email),
|
||||
Name: userModel.Name,
|
||||
Role: role,
|
||||
Currency: setting.Currency,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/akhilrex/hammond/db"
|
||||
"github.com/akhilrex/hammond/models"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
@@ -13,6 +14,7 @@ func CreateVehicle(model models.CreateVehicleRequest, userId string) (*db.Vehicl
|
||||
Nickname: model.Nickname,
|
||||
Registration: model.Registration,
|
||||
Model: model.Model,
|
||||
VIN: model.VIN,
|
||||
Make: model.Make,
|
||||
YearOfManufacture: model.YearOfManufacture,
|
||||
EngineSize: model.EngineSize,
|
||||
@@ -99,6 +101,7 @@ func UpdateVehicle(vehicleID string, model models.UpdateVehicleRequest) error {
|
||||
//return db.DB.Model(&toUpdate).Updates(db.Vehicle{
|
||||
toUpdate.Nickname = model.Nickname
|
||||
toUpdate.Registration = model.Registration
|
||||
toUpdate.VIN = model.VIN
|
||||
toUpdate.Model = model.Model
|
||||
toUpdate.Make = model.Make
|
||||
toUpdate.YearOfManufacture = model.YearOfManufacture
|
||||
@@ -243,6 +246,24 @@ func GetDistinctFuelSubtypesForVehicle(vehicleId string) ([]string, error) {
|
||||
return names, tx.Error
|
||||
}
|
||||
|
||||
func GetLatestOdoReadingForVehicle(vehicleId string) (int, error) {
|
||||
odoReading := 0
|
||||
latestFillup, err := db.GetLatestExpenseByVehicleId(vehicleId)
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return 0, err
|
||||
}
|
||||
odoReading = latestFillup.OdoReading
|
||||
|
||||
latestExpense, err := db.GetLatestExpenseByVehicleId(vehicleId)
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return 0, err
|
||||
}
|
||||
if latestExpense.OdoReading > odoReading {
|
||||
odoReading = latestExpense.OdoReading
|
||||
}
|
||||
return odoReading, nil
|
||||
}
|
||||
|
||||
func GetUserStats(userId string, model models.UserStatsQueryModel) ([]models.VehicleStatsModel, error) {
|
||||
|
||||
vehicles, err := GetUserVehicles(userId)
|
||||
|
||||
28459
ui/package-lock.json
generated
28459
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -34,13 +34,13 @@
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.1",
|
||||
"@fortawesome/vue-fontawesome": "0.1.9",
|
||||
"axios": "0.19.2",
|
||||
"axios": "^0.27.0",
|
||||
"buefy": "^0.9.7",
|
||||
"chart.js": "^2.9.4",
|
||||
"core-js": "3.6.4",
|
||||
"currency-formatter": "^1.5.7",
|
||||
"date-fns": "2.10.0",
|
||||
"lodash": "4.17.15",
|
||||
"lodash": "^4.17.21",
|
||||
"normalize.css": "8.0.1",
|
||||
"nprogress": "0.2.0",
|
||||
"vue": "2.6.11",
|
||||
@@ -70,7 +70,7 @@
|
||||
"hygen": "4.0.x",
|
||||
"imagemin-lint-staged": "0.4.x",
|
||||
"lint-staged": "10.0.x",
|
||||
"markdownlint-cli": "0.22.x",
|
||||
"markdownlint-cli": "^0.31.1",
|
||||
"npm-run-all": "4.1.x",
|
||||
"sass": "1.26.x",
|
||||
"sass-loader": "8.0.x",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 895 B |
@@ -5,6 +5,7 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="shortcut icon" href="<%= webpackConfig.output.publicPath %>hammond.png" />
|
||||
<link rel="apple-touch-icon" href="<%= webpackConfig.output.publicPath %>touch-icon.png" />
|
||||
<title><%= webpackConfig.name %></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
BIN
ui/public/touch-icon.png
Normal file
BIN
ui/public/touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -106,6 +106,7 @@ export default {
|
||||
if (currentDayOfWeek > 1) {
|
||||
toSubtract = -1 * (currentDayOfWeek - 1)
|
||||
}
|
||||
toDate.setHours(0, 0, 0, 0)
|
||||
return addDays(toDate, toSubtract)
|
||||
case 'this_month':
|
||||
return new Date(toDate.getFullYear(), toDate.getMonth(), 1)
|
||||
@@ -114,7 +115,7 @@ export default {
|
||||
case 'past_3_months':
|
||||
return addMonths(toDate, -3)
|
||||
case 'this_year':
|
||||
return new Date(toDate.getFullYear(), 1, 1)
|
||||
return new Date(toDate.getFullYear(), 0, 1)
|
||||
case 'all_time':
|
||||
return new Date(1969, 4, 20)
|
||||
default:
|
||||
|
||||
@@ -47,6 +47,7 @@ export default {
|
||||
fuelUnit: null,
|
||||
fuelType: null,
|
||||
registration: '',
|
||||
vin: '',
|
||||
nickname: '',
|
||||
engineSize: null,
|
||||
make: '',
|
||||
@@ -58,6 +59,7 @@ export default {
|
||||
fuelUnit: veh.fuelUnit,
|
||||
fuelType: veh.fuelType,
|
||||
registration: veh.registration,
|
||||
vin: veh.vin,
|
||||
nickname: veh.nickname,
|
||||
engineSize: veh.engineSize,
|
||||
make: veh.make,
|
||||
@@ -138,6 +140,9 @@ export default {
|
||||
<b-field label="Registration*">
|
||||
<b-input v-model="vehicleModel.registration" type="text" expanded required></b-input>
|
||||
</b-field>
|
||||
<b-field label="VIN">
|
||||
<b-input v-model="vehicleModel.vin" type="text" expanded></b-input>
|
||||
</b-field>
|
||||
<b-field label="Fuel Type*">
|
||||
<b-select v-model.number="vehicleModel.fuelType" placeholder="Fuel Type" required expanded>
|
||||
<option v-for="(option, key) in fuelTypeMasters" :key="key" :value="key">
|
||||
|
||||
@@ -21,13 +21,27 @@ export default {
|
||||
email: '',
|
||||
password: '',
|
||||
distanceUnit: 1,
|
||||
currency: 'INR',
|
||||
currency: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('auth', ['isInitialized']),
|
||||
...mapState('vehicles', ['currencyMasters', 'distanceUnitMasters']),
|
||||
filteredCurrencyMasters() {
|
||||
return this.currencyMasters.filter((option) => {
|
||||
return (
|
||||
option.namePlural
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.indexOf(this.registerModel.currency.toLowerCase()) >= 0 ||
|
||||
option.code
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.indexOf(this.registerModel.currency.toLowerCase()) >= 0
|
||||
)
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
store.dispatch('vehicles/fetchMasters').then((data) => {})
|
||||
@@ -139,6 +153,9 @@ export default {
|
||||
})
|
||||
.finally(() => (this.isWorking = false))
|
||||
},
|
||||
formatCurrency(option) {
|
||||
return `${option.namePlural} (${option.code})`
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -148,15 +165,10 @@ export default {
|
||||
<div v-if="!migrationMode" class="box">
|
||||
<h1 class="title">Migrate from Clarkson</h1>
|
||||
<p>
|
||||
If you have an existing Clarkson deployment and you want to migrate your data from that,
|
||||
press the following button.
|
||||
If you have an existing Clarkson deployment and you want to migrate your data from that, press the following button.
|
||||
</p>
|
||||
<br />
|
||||
<b-field>
|
||||
<b-button type="is-primary" @click="migrationMode = 'clarkson'"
|
||||
>Migrate from Clarkson</b-button
|
||||
></b-field
|
||||
>
|
||||
<b-field> <b-button type="is-primary" @click="migrationMode = 'clarkson'">Migrate from Clarkson</b-button></b-field>
|
||||
</div>
|
||||
<div v-if="!migrationMode" class="box">
|
||||
<h1 class="title">Fresh Install</h1>
|
||||
@@ -170,21 +182,12 @@ export default {
|
||||
</div>
|
||||
<div v-if="migrationMode === 'clarkson'" class="box content">
|
||||
<h1 class="title">Migrate from Clarkson</h1>
|
||||
<p>You need to make sure that this deployment of Hammond can access the MySQL database used by Clarkson.</p>
|
||||
<p>If that is not directly possible, you can make a copy of that database somewhere accessible from this instance.</p>
|
||||
<p>Once that is done, enter the connection string to the MySQL instance in the following format.</p>
|
||||
<p
|
||||
>You need to make sure that this deployment of Hammond can access the MySQL database used by
|
||||
Clarkson.</p
|
||||
>
|
||||
<p
|
||||
>If that is not directly possible, you can make a copy of that database somewhere accessible
|
||||
from this instance.</p
|
||||
>
|
||||
<p
|
||||
>Once that is done, enter the connection string to the MySQL instance in the following
|
||||
format.</p
|
||||
>
|
||||
<p
|
||||
>All the users imported from Clarkson will have their username as their email in Clarkson
|
||||
database and pasword set to <span class="" style="font-weight:bold">hammond</span></p
|
||||
>All the users imported from Clarkson will have their username as their email in Clarkson database and pasword set to
|
||||
<span class="" style="font-weight:bold">hammond</span></p
|
||||
>
|
||||
<code>
|
||||
user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
|
||||
@@ -200,15 +203,8 @@ export default {
|
||||
</b-field>
|
||||
|
||||
<div class="buttons">
|
||||
<b-button
|
||||
v-if="!testSuccess"
|
||||
type="is-primary"
|
||||
:disabled="isWorking"
|
||||
@click="testConnection"
|
||||
>Test Connection</b-button
|
||||
><b-button v-if="testSuccess" type="is-success" :disabled="isWorking" @click="migrate"
|
||||
>Migrate</b-button
|
||||
>
|
||||
<b-button v-if="!testSuccess" type="is-primary" :disabled="isWorking" @click="testConnection">Test Connection</b-button
|
||||
><b-button v-if="testSuccess" type="is-success" :disabled="isWorking" @click="migrate">Migrate</b-button>
|
||||
<b-button type="is-danger is-light" @click="resetMigrationMode">Cancel</b-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -222,28 +218,22 @@ export default {
|
||||
<b-input v-model="registerModel.email" type="email" required></b-input>
|
||||
</b-field>
|
||||
<b-field label="Your Password">
|
||||
<b-input
|
||||
v-model="registerModel.password"
|
||||
type="password"
|
||||
required
|
||||
minlength="8"
|
||||
password-reveal
|
||||
></b-input>
|
||||
<b-input v-model="registerModel.password" type="password" required minlength="8" password-reveal></b-input>
|
||||
</b-field>
|
||||
<b-field label="Currency">
|
||||
<b-select v-model="registerModel.currency" placeholder="Currency" required expanded>
|
||||
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
|
||||
{{ `${option.namePlural} (${option.code})` }}
|
||||
</option>
|
||||
</b-select>
|
||||
<b-autocomplete
|
||||
v-model="registerModel.currency"
|
||||
:custom-formatter="formatCurrency"
|
||||
placeholder="Currency"
|
||||
:data="filteredCurrencyMasters"
|
||||
:keep-first="true"
|
||||
:open-on-focus="true"
|
||||
required
|
||||
@select="(option) => (selected = option)"
|
||||
></b-autocomplete>
|
||||
</b-field>
|
||||
<b-field label="Distance Unit">
|
||||
<b-select
|
||||
v-model.number="registerModel.distanceUnit"
|
||||
placeholder="Distance Unit"
|
||||
required
|
||||
expanded
|
||||
>
|
||||
<b-select v-model.number="registerModel.distanceUnit" placeholder="Distance Unit" required expanded>
|
||||
<option v-for="(option, key) in distanceUnitMasters" :key="key" :value="key">
|
||||
{{ `${option.long} (${option.short})` }}
|
||||
</option>
|
||||
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
password: '',
|
||||
authError: null,
|
||||
tryingToLogIn: false,
|
||||
errorMessage:''
|
||||
errorMessage: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -38,7 +38,7 @@ export default {
|
||||
// and password they provided.
|
||||
tryToLogIn() {
|
||||
this.tryingToLogIn = true
|
||||
this.errorMessage='';
|
||||
this.errorMessage = ''
|
||||
// Reset the authError if it existed.
|
||||
this.authError = null
|
||||
return this.logIn({
|
||||
@@ -53,9 +53,9 @@ export default {
|
||||
// Redirect to the originally requested page, or to the home page
|
||||
})
|
||||
.catch((error) => {
|
||||
if(error.response.data?.errors?.login){
|
||||
this.errorMessage=error.response.data.errors.login
|
||||
}
|
||||
if (error.response.data?.errors?.login) {
|
||||
this.errorMessage = error.response.data.errors.login
|
||||
}
|
||||
this.tryingToLogIn = false
|
||||
this.authError = error
|
||||
})
|
||||
@@ -67,21 +67,9 @@ export default {
|
||||
<template>
|
||||
<Layout>
|
||||
<form @submit.prevent="tryToLogIn">
|
||||
<b-field label="Email">
|
||||
<b-input
|
||||
v-model="username"
|
||||
tag="b-input"
|
||||
name="username"
|
||||
:placeholder="placeholders.username"
|
||||
/></b-field>
|
||||
<b-field label="Email"> <b-input v-model="username" tag="b-input" name="username" type="email" :placeholder="placeholders.username"/></b-field>
|
||||
<b-field label="Password">
|
||||
<b-input
|
||||
v-model="password"
|
||||
tag="b-input"
|
||||
name="password"
|
||||
type="password"
|
||||
:placeholder="placeholders.password"
|
||||
/>
|
||||
<b-input v-model="password" tag="b-input" name="password" type="password" :placeholder="placeholders.password" />
|
||||
</b-field>
|
||||
<b-button tag="input" native-type="submit" :disabled="tryingToLogIn" type="is-primary">
|
||||
<BaseIcon v-if="tryingToLogIn" name="sync" spin />
|
||||
@@ -89,9 +77,7 @@ export default {
|
||||
Log in
|
||||
</span>
|
||||
</b-button>
|
||||
<p v-if="authError">
|
||||
There was an error logging in to your account. {{errorMessage}}
|
||||
</p>
|
||||
<p v-if="authError"> There was an error logging in to your account. {{ errorMessage }} </p>
|
||||
</form>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
@@ -44,6 +44,20 @@ export default {
|
||||
|
||||
return this.changePassModel.new === this.changePassModel.renew
|
||||
},
|
||||
filteredCurrencyMasters() {
|
||||
return this.currencyMasters.filter((option) => {
|
||||
return (
|
||||
option.namePlural
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.indexOf(this.settingsModel.currency.toLowerCase()) >= 0 ||
|
||||
option.code
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.indexOf(this.settingsModel.currency.toLowerCase()) >= 0
|
||||
)
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changePassword() {
|
||||
@@ -109,6 +123,9 @@ export default {
|
||||
this.tryingToSave = false
|
||||
})
|
||||
},
|
||||
formatCurrency(option) {
|
||||
return `${option.namePlural} (${option.code})`
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -123,11 +140,16 @@ export default {
|
||||
These will be used as default values whenever you create a new fillup or expense.
|
||||
</h1>
|
||||
<b-field label="Currency">
|
||||
<b-select v-model="settingsModel.currency" placeholder="Currency" required expanded>
|
||||
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
|
||||
{{ `${option.namePlural} (${option.code})` }}
|
||||
</option>
|
||||
</b-select>
|
||||
<b-autocomplete
|
||||
v-model="settingsModel.currency"
|
||||
:custom-formatter="formatCurrency"
|
||||
placeholder="Currency"
|
||||
:data="filteredCurrencyMasters"
|
||||
:keep-first="true"
|
||||
:open-on-focus="true"
|
||||
required
|
||||
@select="(option) => (selected = option)"
|
||||
></b-autocomplete>
|
||||
</b-field>
|
||||
<b-field label="Distance Unit">
|
||||
<b-select v-model.number="settingsModel.distanceUnit" placeholder="Distance Unit" required expanded>
|
||||
@@ -181,7 +203,7 @@ export default {
|
||||
<table class="table is-hoverable">
|
||||
<tr>
|
||||
<td>Current Version</td>
|
||||
<td>2021.09.20</td>
|
||||
<td>2022.07.06</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Website</td>
|
||||
|
||||
@@ -199,14 +199,21 @@ export default {
|
||||
return
|
||||
}
|
||||
this.tryingToUpload = true
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('file', this.file, this.file.name)
|
||||
formData.append('title', this.title)
|
||||
axios
|
||||
.post(`/api/vehicles/${this.vehicle.id}/attachments`, formData)
|
||||
// const config = { headers: { 'Content-Type': 'multipart/form-data; boundary=' + formData._boundary } }
|
||||
fetch(`/api/vehicles/${this.vehicle.id}/attachments`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
Authorization: this.currentUser.token,
|
||||
},
|
||||
})
|
||||
.then((data) => {
|
||||
this.$buefy.toast.open({
|
||||
message: 'Quick Entry Created Successfully',
|
||||
message: 'File uploaded Successfully',
|
||||
type: 'is-success',
|
||||
duration: 3000,
|
||||
})
|
||||
|
||||
24760
ui/yarn.lock
24760
ui/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user