Skip to content

Commit

Permalink
🗃️ Add audit table
Browse files Browse the repository at this point in the history
  • Loading branch information
tosone committed Jul 22, 2023
1 parent 439ebd5 commit 848a9af
Show file tree
Hide file tree
Showing 25 changed files with 1,212 additions and 55 deletions.
3 changes: 2 additions & 1 deletion pkg/dal/cmd/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func main() {
})

g.ApplyBasic(
models.User{},
models.Audit{},
models.Namespace{},
models.Repository{},
models.Artifact{},
Expand All @@ -36,7 +38,6 @@ func main() {
models.Tag{},
models.Blob{},
models.BlobUpload{},
models.User{},
models.CasbinRule{},
)

Expand Down
94 changes: 94 additions & 0 deletions pkg/dal/dao/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2023 sigma
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dao

import (
"context"
"time"

"github.com/go-sigma/sigma/pkg/dal/models"
"github.com/go-sigma/sigma/pkg/dal/query"
"github.com/go-sigma/sigma/pkg/types/enums"
)

//go:generate mockgen -destination=mocks/audit.go -package=mocks github.com/go-sigma/sigma/pkg/dal/dao AuditService
//go:generate mockgen -destination=mocks/audit_factory.go -package=mocks github.com/go-sigma/sigma/pkg/dal/dao AuditServiceFactory

// AuditService is the interface that provides methods to operate on Audit model
type AuditService interface {
// Create creates a new Audit record in the database
Create(ctx context.Context, audit *models.Audit) error
// HotNamespace get top n hot namespace by user id
HotNamespace(ctx context.Context, userID int64, top int) ([]*models.Namespace, error)
}

type auditService struct {
tx *query.Query
}

// AuditServiceFactory is the interface that provides the audit service factory methods.
type AuditServiceFactory interface {
New(txs ...*query.Query) AuditService
}

type auditServiceFactory struct{}

// NewAuditServiceFactory creates a new audit service factory.
func NewAuditServiceFactory() AuditServiceFactory {
return &auditServiceFactory{}
}

func (f *auditServiceFactory) New(txs ...*query.Query) AuditService {
tx := query.Q
if len(txs) > 0 {
tx = txs[0]
}
return &auditService{
tx: tx,
}
}

// Create create a new artifact if conflict do nothing.
func (s *auditService) Create(ctx context.Context, audit *models.Audit) error {
return s.tx.Audit.WithContext(ctx).Create(audit)
}

// HotNamespace get top n hot namespace by user id
func (s *auditService) HotNamespace(ctx context.Context, userID int64, top int) ([]*models.Namespace, error) {
type result struct {
NamespaceID int64
CreatedAt time.Time
Count int64
}
var rs []result
err := s.tx.Audit.WithContext(ctx).
Where(s.tx.Audit.Action.Neq(enums.AuditActionDelete), s.tx.Audit.UserID.Eq(userID)).
Group(s.tx.Audit.NamespaceID).
Select(s.tx.Audit.NamespaceID, s.tx.Audit.CreatedAt.Max().As(s.tx.Audit.CreatedAt.ColumnName().String()), s.tx.Audit.ID.Count().As("count")).
Limit(top).
UnderlyingDB().
Order("count desc, created_at desc").Find(&rs).Error
if err != nil {
return nil, err
}
if len(rs) == 0 {
return nil, nil
}
var namespaceIDs = make([]int64, 0, len(rs))
for _, audit := range rs {
namespaceIDs = append(namespaceIDs, audit.NamespaceID)
}
return s.tx.Namespace.WithContext(ctx).Where(s.tx.Namespace.ID.In(namespaceIDs...)).Find()
}
50 changes: 50 additions & 0 deletions pkg/dal/dao/mocks/audit.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions pkg/dal/dao/mocks/audit_factory.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions pkg/dal/dao/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ func (s *namespaceService) ListNamespace(ctx context.Context, name *string, pagi
if ok {
switch ptr.To(sort.Method) {
case enums.SortMethodDesc:
query.Order(field.Desc())
query = query.Order(field.Desc())
case enums.SortMethodAsc:
query.Order(field)
query = query.Order(field)
default:
query.Order(s.tx.Namespace.UpdatedAt.Desc())
query = query.Order(s.tx.Namespace.UpdatedAt.Desc())
}
} else {
query.Order(s.tx.Namespace.UpdatedAt.Desc())
query = query.Order(s.tx.Namespace.UpdatedAt.Desc())
}
return query.FindByPage(ptr.To(pagination.Limit)*(ptr.To(pagination.Page)-1), ptr.To(pagination.Limit))
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/dal/dao/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,14 @@ func (s *repositoryService) ListRepository(ctx context.Context, namespaceID int6
if ok {
switch ptr.To(sort.Method) {
case enums.SortMethodDesc:
query.Order(field.Desc())
query = query.Order(field.Desc())
case enums.SortMethodAsc:
query.Order(field)
query = query.Order(field)
default:
query.Order(s.tx.Repository.UpdatedAt.Desc())
query = query.Order(s.tx.Repository.UpdatedAt.Desc())
}
} else {
query.Order(s.tx.Repository.UpdatedAt.Desc())
query = query.Order(s.tx.Repository.UpdatedAt.Desc())
}
return query.FindByPage(ptr.To(pagination.Limit)*(ptr.To(pagination.Page)-1), ptr.To(pagination.Limit))
}
Expand Down
14 changes: 14 additions & 0 deletions pkg/dal/migrations/mysql/0001_initialize.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ CREATE TABLE IF NOT EXISTS `namespaces` (
CONSTRAINT `namespaces_unique_with_name` UNIQUE (`name`, `deleted_at`)
);

CREATE TABLE IF NOT EXISTS `audits` (
`id` bigint AUTO_INCREMENT PRIMARY KEY,
`user_id` bigint NOT NULL,
`namespace_id` bigint NOT NULL,
`action` ENUM ('create', 'update', 'delete', 'pull', 'push') NOT NULL,
`resource_type` ENUM ('namespace', 'repository', 'tag') NOT NULL,
`resource` varchar(256) NOT NULL,
`created_at` timestamp NOT NULL,
`updated_at` timestamp NOT NULL,
`deleted_at` bigint NOT NULL DEFAULT 0,
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
FOREIGN KEY (`namespace_id`) REFERENCES `namespaces` (`id`)
);

CREATE TABLE IF NOT EXISTS `repositories` (
`id` bigint AUTO_INCREMENT PRIMARY KEY,
`name` varchar(64) NOT NULL,
Expand Down
28 changes: 28 additions & 0 deletions pkg/dal/migrations/postgresql/0001_initialize.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ CREATE TABLE IF NOT EXISTS "namespaces" (
CONSTRAINT "namespaces_unique_with_name" UNIQUE ("name", "deleted_at")
);

CREATE TYPE audit_action AS ENUM (
'create',
'update',
'delete',
'pull',
'push'
);

CREATE TYPE audit_resource_type AS ENUM (
'namespace',
'repository',
'tag'
);

CREATE TABLE IF NOT EXISTS "audits" (
"id" bigint AUTO_INCREMENT PRIMARY KEY,
"user_id" bigint NOT NULL,
"namespace_id" bigint NOT NULL,
"action" audit_action NOT NULL,
"resource_type" audit_resource_type NOT NULL,
"resource" varchar(256) NOT NULL,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
"deleted_at" bigint NOT NULL DEFAULT 0,
FOREIGN KEY ("user_id") REFERENCES "users" ("id"),
FOREIGN KEY ("namespace_id") REFERENCES "namespaces" ("id")
);

CREATE TABLE IF NOT EXISTS "repositories" (
"id" bigserial PRIMARY KEY,
"name" varchar(64) NOT NULL,
Expand Down
14 changes: 14 additions & 0 deletions pkg/dal/migrations/sqlite3/0001_initialize.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ CREATE TABLE IF NOT EXISTS `namespaces` (
CONSTRAINT `namespaces_unique_with_name` UNIQUE (`name`, `deleted_at`)
);

CREATE TABLE IF NOT EXISTS `audits` (
`id` bigint AUTO_INCREMENT PRIMARY KEY,
`user_id` bigint NOT NULL,
`namespace_id` bigint NOT NULL,
`action` text CHECK (`action` IN ('create', 'update', 'delete', 'pull', 'push')) NOT NULL,
`resource_type` text CHECK (`resource_type` IN ('namespace', 'repository', 'tag')) NOT NULL,
`resource` varchar(256) NOT NULL,
`created_at` timestamp NOT NULL,
`updated_at` timestamp NOT NULL,
`deleted_at` bigint NOT NULL DEFAULT 0,
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
FOREIGN KEY (`namespace_id`) REFERENCES `namespaces` (`id`)
);

CREATE TABLE IF NOT EXISTS `repositories` (
`id` integer PRIMARY KEY AUTOINCREMENT,
`name` varchar(64) NOT NULL,
Expand Down
40 changes: 40 additions & 0 deletions pkg/dal/models/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2023 sigma
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package models

import (
"time"

"gorm.io/plugin/soft_delete"

"github.com/go-sigma/sigma/pkg/types/enums"
)

// Audit represents a audit
type Audit struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt soft_delete.DeletedAt `gorm:"softDelete:milli"`
ID int64 `gorm:"primaryKey"`

UserID int64
NamespaceID int64
Action enums.AuditAction
ResourceType enums.AuditResourceType
Resource string

Namespace Namespace
User User
}
Loading

0 comments on commit 848a9af

Please sign in to comment.