From 42a9d0d90581f0bb9cf8b9f6c783ff344caaa7c7 Mon Sep 17 00:00:00 2001
From: Wang Yan <wangyan@vmware.com>
Date: Tue, 25 May 2021 11:01:19 +0800
Subject: [PATCH] remove common project code (#14939)

move project model from common to pkg

Signed-off-by: Wang Yan <wangyan@vmware.com>
---
 src/common/dao/base.go                        |  26 +-
 src/common/dao/dao_test.go                    |  95 +-----
 src/common/dao/project.go                     | 303 ------------------
 src/common/dao/project_test.go                | 139 --------
 src/common/models/base.go                     |   1 -
 src/common/models/member.go                   |  38 ---
 src/common/models/project.go                  | 264 ---------------
 src/common/rbac/project/evaluator.go          |   9 +-
 src/common/rbac/project/evaluator_test.go     |   5 +-
 src/common/rbac/project/rbac_user.go          |   2 +-
 src/common/security/local/context_test.go     |  11 +-
 .../security/proxycachesecret/context_test.go |   6 +-
 src/common/security/robot/context.go          |   2 +-
 src/common/security/robot/context_test.go     |  10 +-
 .../event/handler/internal/util_test.go       |  14 +-
 .../handler/webhook/artifact/artifact.go      |   8 +-
 .../handler/webhook/artifact/replication.go   |   4 +-
 .../webhook/artifact/replication_test.go      |   4 +-
 .../event/handler/webhook/chart/chart.go      |   8 +-
 .../event/handler/webhook/chart/chart_test.go |  14 +-
 .../event/handler/webhook/quota/quota.go      |   6 +-
 .../event/handler/webhook/quota/quota_test.go |   4 +-
 .../event/handler/webhook/scan/scan.go        |   8 +-
 src/controller/event/metadata/quota.go        |   4 +-
 src/controller/event/topic.go                 |   4 +-
 src/controller/immutable/controller_test.go   |  11 +-
 src/controller/p2p/preheat/enforcer.go        |  12 +-
 src/controller/p2p/preheat/enforcer_test.go   |   4 +-
 src/controller/proxy/controller.go            |   6 +-
 src/controller/proxy/controller_test.go       |   6 +-
 .../quota/driver/project/project.go           |   4 +-
 src/controller/quota/driver/project/util.go   |   3 +-
 src/controller/quota/util_test.go             |  10 +-
 src/controller/repository/controller_test.go  |   4 +-
 src/controller/robot/controller_test.go       |  14 +-
 src/core/api/chart_label.go                   |   3 +-
 src/core/api/chart_repository_test.go         |   4 +-
 src/core/api/harborapi_test.go                |  59 +---
 src/core/auth/ldap/ldap_test.go               |   7 +-
 src/core/service/token/token_test.go          |   4 +-
 .../job/impl/gc/garbage_collection_test.go    |   8 +-
 src/pkg/exporter/project_collector_test.go    |   5 +-
 src/pkg/project/dao/dao.go                    |   4 +-
 .../{dao/model.go => models/member.go}        |  12 +-
 .../project}/models/pro_meta.go               |   0
 src/pkg/project/models/project.go             | 192 ++++++++++-
 src/pkg/retention/launcher_test.go            |   8 +-
 .../contenttrust/contenttrust_test.go         |  10 +-
 .../middleware/immutable/pushmf_test.go       |  11 +-
 .../middleware/quota/copy_artifact_test.go    |   4 +-
 .../middleware/quota/put_blob_upload_test.go  |   4 +-
 .../middleware/quota/put_manifest_test.go     |   6 +-
 src/server/middleware/quota/quota_test.go     |   4 +-
 src/server/middleware/quota/util_test.go      |   4 +-
 src/server/middleware/repoproxy/proxy.go      |  10 +-
 src/server/middleware/v2auth/auth_test.go     |   8 +-
 .../middleware/vulnerable/vulnerable_test.go  |  12 +-
 src/server/v2.0/handler/project_metadata.go   |  10 +-
 src/testing/apitests/apilib/search.go         |   4 +-
 src/testing/controller/project/controller.go  |  11 +-
 src/testing/pkg/project/manager.go            |   2 +-
 src/testing/suite.go                          |  67 +++-
 62 files changed, 452 insertions(+), 1084 deletions(-)
 delete mode 100644 src/common/dao/project.go
 delete mode 100644 src/common/dao/project_test.go
 delete mode 100644 src/common/models/member.go
 delete mode 100644 src/common/models/project.go
 rename src/pkg/project/{dao/model.go => models/member.go} (80%)
 rename src/{common => pkg/project}/models/pro_meta.go (100%)

diff --git a/src/common/dao/base.go b/src/common/dao/base.go
index 06ee5bdcc..55d59c220 100644
--- a/src/common/dao/base.go
+++ b/src/common/dao/base.go
@@ -17,8 +17,8 @@ package dao
 import (
 	"errors"
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"strconv"
-	"strings"
 	"sync"
 
 	"github.com/astaxie/beego/orm"
@@ -104,17 +104,11 @@ func GetOrmer() orm.Ormer {
 	return globalOrm
 }
 
-// IsDupRecErr checks if the error is due to a duplication of record, currently this
-// works only for pgSQL
-func IsDupRecErr(e error) bool {
-	return strings.Contains(e.Error(), "duplicate key value violates unique constraint")
-}
-
 // ClearTable is the shortcut for test cases, it should be called only in test cases.
 func ClearTable(table string) error {
 	o := GetOrmer()
 	sql := fmt.Sprintf("delete from %s where 1=1", table)
-	if table == models.ProjectTable {
+	if table == proModels.ProjectTable {
 		sql = fmt.Sprintf("delete from %s where project_id > 1", table)
 	}
 	if table == models.UserTable {
@@ -130,22 +124,6 @@ func ClearTable(table string) error {
 	return err
 }
 
-// PaginateForRawSQL ...
-func PaginateForRawSQL(sql string, limit, offset int64) string {
-	return fmt.Sprintf("%s limit %d offset %d", sql, limit, offset)
-}
-
-// PaginateForQuerySetter ...
-func PaginateForQuerySetter(qs orm.QuerySeter, page, size int64) orm.QuerySeter {
-	if size > 0 {
-		qs = qs.Limit(size)
-		if page > 0 {
-			qs = qs.Offset((page - 1) * size)
-		}
-	}
-	return qs
-}
-
 // implements github.com/golang-migrate/migrate/v4.Logger
 type mLogger struct {
 	logger *log.Logger
diff --git a/src/common/dao/dao_test.go b/src/common/dao/dao_test.go
index 3409864d4..12bfb3797 100644
--- a/src/common/dao/dao_test.go
+++ b/src/common/dao/dao_test.go
@@ -16,17 +16,13 @@ package dao
 
 import (
 	"context"
-	"fmt"
-	"os"
-	"testing"
-	"time"
-
 	"github.com/astaxie/beego/orm"
 	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/lib/log"
 	libOrm "github.com/goharbor/harbor/src/lib/orm"
 	"github.com/goharbor/harbor/src/pkg/user"
-	"github.com/stretchr/testify/assert"
+	"os"
+	"testing"
 )
 
 var testCtx context.Context
@@ -99,7 +95,6 @@ func cleanByUser(username string) {
 const username string = "Tester01"
 const password string = "Abc12345"
 const projectName string = "test_project"
-const repositoryName string = "test_repository"
 
 func TestMain(m *testing.M) {
 	databases := []string{"postgresql"}
@@ -153,87 +148,6 @@ func clearAll() {
 	}
 }
 
-var currentUser *models.User
-
-func TestAddProject(t *testing.T) {
-	ctx := libOrm.Context()
-	var err error
-	currentUser, err = user.Mgr.GetByName(ctx, username)
-	if err != nil {
-		t.Errorf("Failed to get user by username: %s, error: %v", username, err)
-	}
-	project := models.Project{
-		OwnerID:      currentUser.UserID,
-		Name:         projectName,
-		CreationTime: time.Now(),
-		OwnerName:    currentUser.Username,
-	}
-
-	_, err = AddProject(project)
-	if err != nil {
-		t.Errorf("Error occurred in AddProject: %v", err)
-	}
-
-	newProject, err := GetProjectByName(projectName)
-	if err != nil {
-		t.Errorf("Error occurred in GetProjectByName: %v", err)
-	}
-	if newProject == nil {
-		t.Errorf("No project found queried by project name: %v", projectName)
-	}
-}
-
-var currentProject *models.Project
-
-func TestGetProject(t *testing.T) {
-	var err error
-	currentProject, err = GetProjectByName(projectName)
-	if err != nil {
-		t.Errorf("Error occurred in GetProjectByName: %v", err)
-	}
-	if currentProject == nil {
-		t.Errorf("No project found queried by project name: %v", projectName)
-	}
-	if currentProject.Name != projectName {
-		t.Errorf("Project name does not match, expected: %s, actual: %s", projectName, currentProject.Name)
-	}
-}
-
-func TestGetProjectById(t *testing.T) {
-	id := currentProject.ProjectID
-	p, err := GetProjectByID(id)
-	if err != nil {
-		t.Errorf("Error in GetProjectById: %v, id: %d", err, id)
-	}
-	if p.Name != currentProject.Name {
-		t.Errorf("project name does not match, expected: %s, actual: %s", currentProject.Name, p.Name)
-	}
-}
-
-func TestGetTotalOfProjects(t *testing.T) {
-	total, err := GetTotalOfProjects(nil)
-	if err != nil {
-		t.Fatalf("failed to get total of projects: %v", err)
-	}
-
-	if total != 2 {
-		t.Errorf("unexpected total: %d != 2", total)
-	}
-}
-
-func TestGetProjects(t *testing.T) {
-	projects, err := GetProjects(nil)
-	if err != nil {
-		t.Errorf("Error occurred in GetProjects: %v", err)
-	}
-	if len(projects) != 2 {
-		t.Errorf("Expected length of projects is 2, but actual: %d, the projects: %+v", len(projects), projects)
-	}
-	if projects[1].Name != projectName {
-		t.Errorf("Expected project name in the list: %s, actual: %s", projectName, projects[1].Name)
-	}
-}
-
 var targetID, policyID, policyID2, policyID3, jobID, jobID2, jobID3 int64
 
 func TestGetOrmer(t *testing.T) {
@@ -242,8 +156,3 @@ func TestGetOrmer(t *testing.T) {
 		t.Errorf("Error get ormer.")
 	}
 }
-
-func TestIsDupRecError(t *testing.T) {
-	assert.True(t, IsDupRecErr(fmt.Errorf("pq: duplicate key value violates unique constraint \"properties_k_key\"")))
-	assert.False(t, IsDupRecErr(fmt.Errorf("other error")))
-}
diff --git a/src/common/dao/project.go b/src/common/dao/project.go
deleted file mode 100644
index 9d754fd84..000000000
--- a/src/common/dao/project.go
+++ /dev/null
@@ -1,303 +0,0 @@
-// Copyright Project Harbor Authors
-//
-// 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 (
-	"github.com/goharbor/harbor/src/common"
-	"github.com/goharbor/harbor/src/common/models"
-	"github.com/goharbor/harbor/src/common/utils"
-	"github.com/goharbor/harbor/src/lib/log"
-	libOrm "github.com/goharbor/harbor/src/lib/orm"
-
-	"fmt"
-	"time"
-)
-
-// AddProject adds a project to the database along with project roles information and access log records.
-func AddProject(project models.Project) (int64, error) {
-	o := GetOrmer()
-
-	sql := "insert into project (owner_id, name, registry_id, creation_time, update_time, deleted) values (?, ?, ?, ?, ?, ?) RETURNING project_id"
-	var projectID int64
-	now := time.Now()
-
-	err := o.Raw(sql, project.OwnerID, project.Name, project.RegistryID, now, now, project.Deleted).QueryRow(&projectID)
-	if err != nil {
-		return 0, err
-	}
-
-	pmID, err := addProjectMember(models.Member{
-		ProjectID:  projectID,
-		EntityID:   project.OwnerID,
-		Role:       common.RoleProjectAdmin,
-		EntityType: common.UserMember,
-	})
-	if err != nil {
-		return 0, err
-	}
-	if pmID == 0 {
-		return projectID, err
-	}
-	return projectID, nil
-}
-
-func addProjectMember(member models.Member) (int, error) {
-
-	log.Debugf("Adding project member %+v", member)
-
-	o := GetOrmer()
-
-	if member.EntityID <= 0 {
-		return 0, fmt.Errorf("invalid entity_id, member: %+v", member)
-	}
-
-	if member.ProjectID <= 0 {
-		return 0, fmt.Errorf("invalid project_id, member: %+v", member)
-	}
-
-	var pmID int
-	sql := "insert into project_member (project_id, entity_id , role, entity_type) values (?, ?, ?, ?) RETURNING id"
-	err := o.Raw(sql, member.ProjectID, member.EntityID, member.Role, member.EntityType).QueryRow(&pmID)
-	if err != nil {
-		return 0, err
-	}
-	return pmID, err
-}
-
-// GetProjectByID ...
-func GetProjectByID(id int64) (*models.Project, error) {
-	o := GetOrmer()
-
-	sql := `select p.project_id, p.name, p.registry_id, u.username as owner_name, p.owner_id, p.creation_time, p.update_time
-		from project p left join harbor_user u on p.owner_id = u.user_id where p.deleted = false and p.project_id = ?`
-	queryParam := make([]interface{}, 1)
-	queryParam = append(queryParam, id)
-
-	p := []models.Project{}
-	count, err := o.Raw(sql, queryParam).QueryRows(&p)
-
-	if err != nil {
-		return nil, err
-	}
-
-	if count == 0 {
-		return nil, nil
-	}
-
-	return &p[0], nil
-}
-
-// GetProjectByName ...
-func GetProjectByName(name string) (*models.Project, error) {
-	o := GetOrmer()
-	var p []models.Project
-	n, err := o.Raw(`select * from project where name = ? and deleted = false`, name).QueryRows(&p)
-	if err != nil {
-		return nil, err
-	}
-
-	if n == 0 {
-		return nil, nil
-	}
-
-	return &p[0], nil
-}
-
-// ProjectExistsByName returns whether the project exists according to its name.
-func ProjectExistsByName(name string) bool {
-	o := GetOrmer()
-	return o.QueryTable("project").Filter("name", name).Exist()
-}
-
-// GetTotalOfProjects returns the total count of projects
-// according to the query conditions
-func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
-	var pagination *models.Pagination
-	if query != nil {
-		pagination = query.Pagination
-		query.Pagination = nil
-	}
-	sql, params := projectQueryConditions(query)
-	if query != nil {
-		query.Pagination = pagination
-	}
-
-	sql = `select count(*) ` + sql
-
-	var total int64
-	err := GetOrmer().Raw(sql, params).QueryRow(&total)
-	return total, err
-}
-
-// GetProjects returns a project list according to the query conditions
-func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
-	sqlStr, queryParam := projectQueryConditions(query)
-	sqlStr = `select distinct p.project_id, p.name, p.registry_id, p.owner_id,
-		p.creation_time, p.update_time ` + sqlStr + ` order by p.name`
-	sqlStr, queryParam = CreatePagination(query, sqlStr, queryParam)
-
-	var projects []*models.Project
-	_, err := GetOrmer().Raw(sqlStr, queryParam).QueryRows(&projects)
-
-	return projects, err
-
-}
-
-// GetGroupProjects - Get user's all projects, including user is the user member of this project
-// and the user is in the group which is a group member of this project.
-func GetGroupProjects(groupIDs []int, query *models.ProjectQueryParam) ([]*models.Project, error) {
-	sql, params := projectQueryConditions(query)
-	sql = `select distinct p.project_id, p.name, p.registry_id, p.owner_id,
-				p.creation_time, p.update_time ` + sql
-	groupIDCondition := JoinNumberConditions(groupIDs)
-	if len(groupIDs) > 0 {
-		sql = fmt.Sprintf(
-			`%s union select distinct p.project_id, p.name, p.registry_id, p.owner_id, p.creation_time, p.update_time
-		     from project p
-		     left join project_member pm on p.project_id = pm.project_id
-		     left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g'
-			 where p.deleted=false and ug.id in ( %s )`,
-			sql, groupIDCondition)
-	}
-	sql = sql + ` order by name`
-	sqlStr, queryParams := CreatePagination(query, sql, params)
-	log.Debugf("query sql:%v", sql)
-	var projects []*models.Project
-	_, err := GetOrmer().Raw(sqlStr, queryParams).QueryRows(&projects)
-	return projects, err
-}
-
-// GetTotalGroupProjects - Get the total count of projects, including  user is the member of this project and the
-// user is in the group, which is the group member of this project.
-func GetTotalGroupProjects(groupIDs []int, query *models.ProjectQueryParam) (int, error) {
-	var sql string
-	sqlCondition, params := projectQueryConditions(query)
-	groupIDCondition := JoinNumberConditions(groupIDs)
-	if len(groupIDs) == 0 {
-		sql = `select count(1) ` + sqlCondition
-	} else {
-		sql = fmt.Sprintf(
-			`select count(1)
-			   from ( select  p.project_id %s  union select  p.project_id
-			   from project p
-			   left join project_member pm on p.project_id = pm.project_id
-			   left join user_group ug on ug.id = pm.entity_id and pm.entity_type = 'g'
-			   where p.deleted=false and ug.id in ( %s )) t`,
-			sqlCondition, groupIDCondition)
-	}
-	log.Debugf("query sql:%v", sql)
-	var count int
-	if err := GetOrmer().Raw(sql, params).QueryRow(&count); err != nil {
-		return 0, err
-	}
-	return count, nil
-}
-
-func projectQueryConditions(query *models.ProjectQueryParam) (string, []interface{}) {
-	params := []interface{}{}
-	sql := ` from project as p`
-	if query == nil {
-		sql += ` where p.deleted=false`
-		return sql, params
-	}
-	// if query.ProjectIDs is not nil but has no element, the query will returns no rows
-	if query.ProjectIDs != nil && len(query.ProjectIDs) == 0 {
-		sql += ` where 1 = 0`
-		return sql, params
-	}
-	if len(query.Owner) != 0 {
-		sql += ` join harbor_user u1
-					on p.owner_id = u1.user_id`
-	}
-	if query.Member != nil && len(query.Member.Name) != 0 {
-		sql += ` join project_member pm
-					on p.project_id = pm.project_id and pm.entity_type = 'u'
-					join harbor_user u2
-					on pm.entity_id=u2.user_id`
-	}
-	sql += ` where p.deleted=false`
-
-	if len(query.Owner) != 0 {
-		sql += ` and u1.username=?`
-		params = append(params, query.Owner)
-	}
-
-	if len(query.Name) != 0 {
-		sql += ` and p.name like ?`
-		params = append(params, "%"+libOrm.Escape(query.Name)+"%")
-	}
-
-	if query.RegistryID > 0 {
-		sql += ` and p.registry_id = ?`
-		params = append(params, query.RegistryID)
-	}
-
-	if query.Member != nil && len(query.Member.Name) != 0 {
-		sql += ` and u2.username=?`
-		params = append(params, query.Member.Name)
-
-		if query.Member.Role > 0 {
-			sql += ` and pm.role = ?`
-			roleID := 0
-			switch query.Member.Role {
-			case common.RoleProjectAdmin:
-				roleID = 1
-			case common.RoleDeveloper:
-				roleID = 2
-			case common.RoleGuest:
-				roleID = 3
-			case common.RoleMaintainer:
-				roleID = 4
-			case common.RoleLimitedGuest:
-				roleID = 5
-			}
-			params = append(params, roleID)
-		}
-	}
-	if len(query.ProjectIDs) > 0 {
-		sql += fmt.Sprintf(` and p.project_id in ( %s )`,
-			utils.ParamPlaceholderForIn(len(query.ProjectIDs)))
-		params = append(params, query.ProjectIDs)
-	}
-	return sql, params
-}
-
-// CreatePagination ...
-func CreatePagination(query *models.ProjectQueryParam, sql string, params []interface{}) (string, []interface{}) {
-	if query != nil && query.Pagination != nil && query.Pagination.Size > 0 {
-		sql += ` limit ?`
-		params = append(params, query.Pagination.Size)
-
-		if query.Pagination.Page > 0 {
-			sql += ` offset ?`
-			params = append(params, (query.Pagination.Page-1)*query.Pagination.Size)
-		}
-	}
-	return sql, params
-}
-
-// DeleteProject ...
-func DeleteProject(id int64) error {
-	project, err := GetProjectByID(id)
-	if err != nil {
-		return err
-	}
-	name := fmt.Sprintf("%s#%d", project.Name, project.ProjectID)
-	sql := `update project
-		set deleted = true, name = ?
-		where project_id = ?`
-	_, err = GetOrmer().Raw(sql, name, id).Exec()
-	return err
-}
diff --git a/src/common/dao/project_test.go b/src/common/dao/project_test.go
deleted file mode 100644
index 4fd439a77..000000000
--- a/src/common/dao/project_test.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright Project Harbor Authors
-//
-// 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 (
-	"fmt"
-	"strings"
-	"testing"
-
-	"github.com/goharbor/harbor/src/common/models"
-)
-
-func TestDeleteProject(t *testing.T) {
-	name := "project_for_test"
-	project := models.Project{
-		OwnerID: currentUser.UserID,
-		Name:    name,
-	}
-
-	id, err := AddProject(project)
-	if err != nil {
-		t.Fatalf("failed to add project: %v", err)
-	}
-	defer func() {
-		if err := delProjPermanent(id); err != nil {
-			t.Errorf("failed to clear up project %d: %v", id, err)
-		}
-	}()
-
-	if err = DeleteProject(id); err != nil {
-		t.Fatalf("failed to delete project: %v", err)
-	}
-
-	p := &models.Project{}
-	if err = GetOrmer().Raw(`select * from project where project_id = ?`, id).
-		QueryRow(p); err != nil {
-		t.Fatalf("failed to get project: %v", err)
-	}
-
-	if !p.Deleted {
-		t.Errorf("unexpeced deleted column: %t != %t", p.Deleted, true)
-	}
-
-	deletedName := fmt.Sprintf("%s#%d", name, id)
-	if p.Name != deletedName {
-		t.Errorf("unexpected name: %s != %s", p.Name, deletedName)
-	}
-
-}
-
-func delProjPermanent(id int64) error {
-	_, err := GetOrmer().Raw(`delete from project_member 
-		where project_id = ?`, id).Exec()
-	if err != nil {
-		return err
-	}
-
-	_, err = GetOrmer().QueryTable("project").
-		Filter("ProjectID", id).
-		Delete()
-	return err
-}
-
-func Test_projectQueryConditions(t *testing.T) {
-	type args struct {
-		query *models.ProjectQueryParam
-	}
-	tests := []struct {
-		name  string
-		args  args
-		want  string
-		want1 []interface{}
-	}{
-		{"Query invalid projectID",
-			args{query: &models.ProjectQueryParam{ProjectIDs: []int64{}, Owner: "admin"}},
-			"from project as p where 1 = 0",
-			[]interface{}{}},
-		{"Query with valid projectID",
-			args{query: &models.ProjectQueryParam{ProjectIDs: []int64{2, 3}, Owner: "admin"}},
-			` from project as p join harbor_user u1
-					on p.owner_id = u1.user_id where p.deleted=false and u1.username=? and p.project_id in ( ?,? )`,
-			[]interface{}{2, 3}},
-		{"Query with valid page and member",
-			args{query: &models.ProjectQueryParam{ProjectIDs: []int64{2, 3}, Owner: "admin", Name: "sample", Member: &models.MemberQuery{Name: "name", Role: 1}, Pagination: &models.Pagination{Page: 1, Size: 20}}},
-			` from project as p join harbor_user u1
-					on p.owner_id = u1.user_id join project_member pm
-					on p.project_id = pm.project_id and pm.entity_type = 'u'
-					join harbor_user u2
-					on pm.entity_id=u2.user_id where p.deleted=false and u1.username=? and p.name like ? and u2.username=? and pm.role = ? and p.project_id in ( ?,? )`,
-			[]interface{}{1, []int64{2, 3}, 20, 0}},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got, _ := projectQueryConditions(tt.args.query)
-			if strings.TrimSpace(got) != strings.TrimSpace(tt.want) {
-				t.Errorf("projectQueryConditions() got = %v\n, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
-func TestProjetExistsByName(t *testing.T) {
-	name := "project_exist_by_name_test"
-	exist := ProjectExistsByName(name)
-	if exist {
-		t.Errorf("project %s expected to be not exist", name)
-	}
-
-	project := models.Project{
-		OwnerID: currentUser.UserID,
-		Name:    name,
-	}
-	id, err := AddProject(project)
-	if err != nil {
-		t.Fatalf("failed to add project: %v", err)
-	}
-	defer func() {
-		if err := delProjPermanent(id); err != nil {
-			t.Errorf("failed to clear up project %d: %v", id, err)
-		}
-	}()
-
-	exist = ProjectExistsByName(name)
-	if !exist {
-		t.Errorf("project %s expected to be exist", name)
-	}
-}
diff --git a/src/common/models/base.go b/src/common/models/base.go
index d935e790c..c290c687d 100644
--- a/src/common/models/base.go
+++ b/src/common/models/base.go
@@ -21,7 +21,6 @@ import (
 func init() {
 	orm.RegisterModel(
 		new(User),
-		new(Project),
 		new(Role),
 		new(ResourceLabel),
 		new(JobLog),
diff --git a/src/common/models/member.go b/src/common/models/member.go
deleted file mode 100644
index 24aab0aba..000000000
--- a/src/common/models/member.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright Project Harbor Authors
-//
-// 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 (
-	"github.com/goharbor/harbor/src/pkg/usergroup/model"
-)
-
-// Member holds the details of a member.
-type Member struct {
-	ID         int    `orm:"pk;column(id)" json:"id"`
-	ProjectID  int64  `orm:"column(project_id)" json:"project_id"`
-	Entityname string `orm:"column(entity_name)" json:"entity_name"`
-	Rolename   string `json:"role_name"`
-	Role       int    `json:"role_id"`
-	EntityID   int    `orm:"column(entity_id)" json:"entity_id"`
-	EntityType string `orm:"column(entity_type)" json:"entity_type"`
-}
-
-// MemberReq -  Create Project Member Request
-type MemberReq struct {
-	ProjectID   int64           `json:"project_id"`
-	Role        int             `json:"role_id,omitempty"`
-	MemberUser  User            `json:"member_user,omitempty"`
-	MemberGroup model.UserGroup `json:"member_group,omitempty"`
-}
diff --git a/src/common/models/project.go b/src/common/models/project.go
deleted file mode 100644
index e8cec85db..000000000
--- a/src/common/models/project.go
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright Project Harbor Authors
-//
-// 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 (
-	"context"
-	"fmt"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/astaxie/beego/orm"
-	"github.com/goharbor/harbor/src/pkg/allowlist/models"
-	"github.com/lib/pq"
-)
-
-const (
-	// ProjectTable is the table name for project
-	ProjectTable = "project"
-	// ProjectPublic means project is public
-	ProjectPublic = "public"
-	// ProjectPrivate means project is private
-	ProjectPrivate = "private"
-)
-
-// Project holds the details of a project.
-type Project struct {
-	ProjectID    int64               `orm:"pk;auto;column(project_id)" json:"project_id"`
-	OwnerID      int                 `orm:"column(owner_id)" json:"owner_id"`
-	Name         string              `orm:"column(name)" json:"name" sort:"default"`
-	CreationTime time.Time           `orm:"column(creation_time);auto_now_add" json:"creation_time"`
-	UpdateTime   time.Time           `orm:"column(update_time);auto_now" json:"update_time"`
-	Deleted      bool                `orm:"column(deleted)" json:"deleted"`
-	OwnerName    string              `orm:"-" json:"owner_name"`
-	Role         int                 `orm:"-" json:"current_user_role_id"`
-	RoleList     []int               `orm:"-" json:"current_user_role_ids"`
-	RepoCount    int64               `orm:"-" json:"repo_count"`
-	ChartCount   uint64              `orm:"-" json:"chart_count"`
-	Metadata     map[string]string   `orm:"-" json:"metadata"`
-	CVEAllowlist models.CVEAllowlist `orm:"-" json:"cve_allowlist"`
-	RegistryID   int64               `orm:"column(registry_id)" json:"registry_id"`
-}
-
-// GetMetadata ...
-func (p *Project) GetMetadata(key string) (string, bool) {
-	if len(p.Metadata) == 0 {
-		return "", false
-	}
-	value, exist := p.Metadata[key]
-	return value, exist
-}
-
-// SetMetadata ...
-func (p *Project) SetMetadata(key, value string) {
-	if p.Metadata == nil {
-		p.Metadata = map[string]string{}
-	}
-	p.Metadata[key] = value
-}
-
-// IsPublic ...
-func (p *Project) IsPublic() bool {
-	public, exist := p.GetMetadata(ProMetaPublic)
-	if !exist {
-		return false
-	}
-
-	return isTrue(public)
-}
-
-// IsProxy returns true when the project type is proxy cache
-func (p *Project) IsProxy() bool {
-	return p.RegistryID > 0
-}
-
-// ContentTrustEnabled ...
-func (p *Project) ContentTrustEnabled() bool {
-	enabled, exist := p.GetMetadata(ProMetaEnableContentTrust)
-	if !exist {
-		return false
-	}
-	return isTrue(enabled)
-}
-
-// VulPrevented ...
-func (p *Project) VulPrevented() bool {
-	prevent, exist := p.GetMetadata(ProMetaPreventVul)
-	if !exist {
-		return false
-	}
-	return isTrue(prevent)
-}
-
-// ReuseSysCVEAllowlist ...
-func (p *Project) ReuseSysCVEAllowlist() bool {
-	r, ok := p.GetMetadata(ProMetaReuseSysCVEAllowlist)
-	if !ok {
-		return true
-	}
-	return isTrue(r)
-}
-
-// Severity ...
-func (p *Project) Severity() string {
-	severity, exist := p.GetMetadata(ProMetaSeverity)
-	if !exist {
-		return ""
-	}
-	return severity
-}
-
-// AutoScan ...
-func (p *Project) AutoScan() bool {
-	auto, exist := p.GetMetadata(ProMetaAutoScan)
-	if !exist {
-		return false
-	}
-	return isTrue(auto)
-}
-
-// FilterByPublic returns orm.QuerySeter with public filter
-func (p *Project) FilterByPublic(ctx context.Context, qs orm.QuerySeter, key string, value interface{}) orm.QuerySeter {
-	subQuery := `SELECT project_id FROM project_metadata WHERE name = 'public' AND value = '%s'`
-	if isTrue(value) {
-		subQuery = fmt.Sprintf(subQuery, "true")
-	} else {
-		subQuery = fmt.Sprintf(subQuery, "false")
-	}
-	return qs.FilterRaw("project_id", fmt.Sprintf("IN (%s)", subQuery))
-}
-
-// FilterByOwner returns orm.QuerySeter with owner filter
-func (p *Project) FilterByOwner(ctx context.Context, qs orm.QuerySeter, key string, value interface{}) orm.QuerySeter {
-	username, ok := value.(string)
-	if !ok {
-		return qs
-	}
-
-	return qs.FilterRaw("owner_id", fmt.Sprintf("IN (SELECT user_id FROM harbor_user WHERE username = %s)", pq.QuoteLiteral(username)))
-}
-
-// FilterByMember returns orm.QuerySeter with member filter
-func (p *Project) FilterByMember(ctx context.Context, qs orm.QuerySeter, key string, value interface{}) orm.QuerySeter {
-	query, ok := value.(*MemberQuery)
-	if !ok {
-		return qs
-	}
-	subQuery := fmt.Sprintf(`SELECT project_id FROM project_member WHERE entity_id = %d AND entity_type = 'u'`, query.UserID)
-	if query.Role > 0 {
-		subQuery = fmt.Sprintf("%s AND role = %d", subQuery, query.Role)
-	}
-
-	if query.WithPublic {
-		subQuery = fmt.Sprintf("(%s) UNION (SELECT project_id FROM project_metadata WHERE name = 'public' AND value = 'true')", subQuery)
-	}
-
-	if len(query.GroupIDs) > 0 {
-		var elems []string
-		for _, groupID := range query.GroupIDs {
-			elems = append(elems, strconv.Itoa(groupID))
-		}
-
-		tpl := "(%s) UNION (SELECT project_id FROM project_member pm, user_group ug WHERE pm.entity_id = ug.id AND pm.entity_type = 'g' AND ug.id IN (%s))"
-		subQuery = fmt.Sprintf(tpl, subQuery, strings.TrimSpace(strings.Join(elems, ", ")))
-	}
-
-	return qs.FilterRaw("project_id", fmt.Sprintf("IN (%s)", subQuery))
-}
-
-func isTrue(i interface{}) bool {
-	switch value := i.(type) {
-	case bool:
-		return value
-	case string:
-		v := strings.ToLower(value)
-		return v == "true" || v == "1"
-	default:
-		return false
-	}
-}
-
-// ProjectQueryParam can be used to set query parameters when listing projects.
-// The query condition will be set in the query if its corresponding field
-// is not nil. Leave it empty if you don't want to apply this condition.
-//
-// e.g.
-// List all projects: query := nil
-// List all public projects: query := &QueryParam{Public: true}
-// List projects the owner of which is user1: query := &QueryParam{Owner:"user1"}
-// List all public projects the owner of which is user1: query := &QueryParam{Owner:"user1",Public:true}
-// List projects which user1 is member of: query := &QueryParam{Member:&Member{Name:"user1"}}
-// List projects which user1 is the project admin : query := &QueryParam{Member:&Member{Name:"user1",Role:1}}
-type ProjectQueryParam struct {
-	Name       string // the name of project
-	Owner      string // the username of project owner
-	Public     *bool  // the project is public or not, can be ture, false and nil
-	RegistryID int64
-	Member     *MemberQuery // the member of project
-	Pagination *Pagination  // pagination information
-	ProjectIDs []int64      // project ID list
-}
-
-// MemberQuery filter by member's username and role
-type MemberQuery struct {
-	UserID   int    // the user id
-	Name     string // the username of member
-	Role     int    // the role of the member has to the project
-	GroupIDs []int  // the group ID of current user belongs to
-
-	WithPublic bool // include the public projects for the member
-}
-
-// Pagination ...
-type Pagination struct {
-	Page int64
-	Size int64
-}
-
-// Sorting sort by given field, ascending or descending
-type Sorting struct {
-	Sort string // in format [+-]?<FIELD_NAME>, e.g. '+creation_time', '-creation_time'
-}
-
-// BaseProjectCollection contains the query conditions which can be used
-// to get a project collection. The collection can be used as the base to
-// do other filter
-type BaseProjectCollection struct {
-	Public bool
-	Member string
-}
-
-// ProjectRequest holds informations that need for creating project API
-type ProjectRequest struct {
-	Name         string              `json:"project_name"`
-	Public       *int                `json:"public"` // deprecated, reserved for project creation in replication
-	Metadata     map[string]string   `json:"metadata"`
-	CVEAllowlist models.CVEAllowlist `json:"cve_allowlist"`
-
-	StorageLimit *int64 `json:"storage_limit,omitempty"`
-	RegistryID   int64  `json:"registry_id"`
-}
-
-// ProjectQueryResult ...
-type ProjectQueryResult struct {
-	Total    int64
-	Projects []*Project
-}
-
-// TableName is required by beego orm to map Project to table project
-func (p *Project) TableName() string {
-	return ProjectTable
-}
diff --git a/src/common/rbac/project/evaluator.go b/src/common/rbac/project/evaluator.go
index d00c8b649..023c91f84 100644
--- a/src/common/rbac/project/evaluator.go
+++ b/src/common/rbac/project/evaluator.go
@@ -24,14 +24,15 @@ import (
 	"github.com/goharbor/harbor/src/pkg/permission/evaluator/namespace"
 	"github.com/goharbor/harbor/src/pkg/permission/evaluator/rbac"
 	"github.com/goharbor/harbor/src/pkg/permission/types"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 )
 
 // RBACUserBuilder builder to make types.RBACUser for the project
-type RBACUserBuilder func(context.Context, *models.Project) types.RBACUser
+type RBACUserBuilder func(context.Context, *proModels.Project) types.RBACUser
 
 // NewBuilderForUser create a builder for the local user
 func NewBuilderForUser(user *models.User, ctl project.Controller) RBACUserBuilder {
-	return func(ctx context.Context, p *models.Project) types.RBACUser {
+	return func(ctx context.Context, p *proModels.Project) types.RBACUser {
 		if user == nil {
 			// anonymous access
 			return &rbacUser{
@@ -56,9 +57,9 @@ func NewBuilderForUser(user *models.User, ctl project.Controller) RBACUserBuilde
 
 // NewBuilderForPolicies create a builder for the policies
 func NewBuilderForPolicies(username string, policies []*types.Policy,
-	filters ...func(*models.Project, []*types.Policy) []*types.Policy) RBACUserBuilder {
+	filters ...func(*proModels.Project, []*types.Policy) []*types.Policy) RBACUserBuilder {
 
-	return func(ctx context.Context, p *models.Project) types.RBACUser {
+	return func(ctx context.Context, p *proModels.Project) types.RBACUser {
 		for _, filter := range filters {
 			policies = filter(p, policies)
 		}
diff --git a/src/common/rbac/project/evaluator_test.go b/src/common/rbac/project/evaluator_test.go
index d4ab2e7d8..031aaf022 100644
--- a/src/common/rbac/project/evaluator_test.go
+++ b/src/common/rbac/project/evaluator_test.go
@@ -21,13 +21,14 @@ import (
 
 	"github.com/goharbor/harbor/src/common"
 	"github.com/goharbor/harbor/src/common/models"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
 	"github.com/goharbor/harbor/src/testing/mock"
 	"github.com/stretchr/testify/assert"
 )
 
 var (
-	public = &models.Project{
+	public = &proModels.Project{
 		ProjectID: 1,
 		Name:      "public_project",
 		OwnerID:   1,
@@ -36,7 +37,7 @@ var (
 		},
 	}
 
-	private = &models.Project{
+	private = &proModels.Project{
 		ProjectID: 2,
 		Name:      "private_project",
 		OwnerID:   1,
diff --git a/src/common/rbac/project/rbac_user.go b/src/common/rbac/project/rbac_user.go
index 76a1310e5..6a716de78 100644
--- a/src/common/rbac/project/rbac_user.go
+++ b/src/common/rbac/project/rbac_user.go
@@ -15,8 +15,8 @@
 package project
 
 import (
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/pkg/permission/types"
+	"github.com/goharbor/harbor/src/pkg/project/models"
 )
 
 type rbacUser struct {
diff --git a/src/common/security/local/context_test.go b/src/common/security/local/context_test.go
index 4e9404967..359020f41 100644
--- a/src/common/security/local/context_test.go
+++ b/src/common/security/local/context_test.go
@@ -16,20 +16,19 @@ package local
 
 import (
 	"context"
-	rbac_project "github.com/goharbor/harbor/src/common/rbac/project"
-	"testing"
-
 	"github.com/goharbor/harbor/src/common"
 	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/rbac"
-	"github.com/goharbor/harbor/src/controller/project"
+	rbac_project "github.com/goharbor/harbor/src/common/rbac/project"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
 	"github.com/goharbor/harbor/src/testing/mock"
 	"github.com/stretchr/testify/assert"
+	"testing"
 )
 
 var (
-	public = &project.Project{
+	public = &proModels.Project{
 		ProjectID: 1,
 		Name:      "public_project",
 		OwnerID:   1,
@@ -38,7 +37,7 @@ var (
 		},
 	}
 
-	private = &models.Project{
+	private = &proModels.Project{
 		ProjectID: 2,
 		Name:      "private_project",
 		OwnerID:   1,
diff --git a/src/common/security/proxycachesecret/context_test.go b/src/common/security/proxycachesecret/context_test.go
index 87e67096b..3cb41e4ca 100644
--- a/src/common/security/proxycachesecret/context_test.go
+++ b/src/common/security/proxycachesecret/context_test.go
@@ -20,8 +20,8 @@ import (
 	"github.com/goharbor/harbor/src/common/rbac/project"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/rbac"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
 	"github.com/goharbor/harbor/src/testing/mock"
 	"github.com/stretchr/testify/suite"
@@ -85,7 +85,7 @@ func (p *proxyCacheSecretTestSuite) TestCan() {
 	// pass for action pull
 	action = rbac.ActionPull
 	resource = project.NewNamespace(1).Resource(rbac.ResourceRepository)
-	p.ctl.On("Get", mock.Anything, mock.Anything).Return(&models.Project{
+	p.ctl.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{
 		ProjectID: 1,
 		Name:      "library",
 	}, nil)
@@ -98,7 +98,7 @@ func (p *proxyCacheSecretTestSuite) TestCan() {
 	// pass for action push
 	action = rbac.ActionPush
 	resource = project.NewNamespace(1).Resource(rbac.ResourceRepository)
-	p.ctl.On("Get", mock.Anything, mock.Anything).Return(&models.Project{
+	p.ctl.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{
 		ProjectID: 1,
 		Name:      "library",
 	}, nil)
diff --git a/src/common/security/robot/context.go b/src/common/security/robot/context.go
index 69eeba24b..14b9350a2 100644
--- a/src/common/security/robot/context.go
+++ b/src/common/security/robot/context.go
@@ -22,11 +22,11 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/rbac"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/pkg/permission/evaluator"
 	"github.com/goharbor/harbor/src/pkg/permission/types"
+	"github.com/goharbor/harbor/src/pkg/project/models"
 	"github.com/goharbor/harbor/src/pkg/robot/model"
 )
 
diff --git a/src/common/security/robot/context_test.go b/src/common/security/robot/context_test.go
index c14547bf8..871b6f83f 100644
--- a/src/common/security/robot/context_test.go
+++ b/src/common/security/robot/context_test.go
@@ -21,9 +21,9 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/rbac"
 	"github.com/goharbor/harbor/src/pkg/permission/types"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"github.com/goharbor/harbor/src/pkg/robot/model"
 	projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
 	"github.com/goharbor/harbor/src/testing/mock"
@@ -31,7 +31,7 @@ import (
 )
 
 var (
-	private = &models.Project{
+	private = &proModels.Project{
 		Name:    "testrobot",
 		OwnerID: 1,
 	}
@@ -150,7 +150,7 @@ func TestHasPushPullPerm(t *testing.T) {
 
 func Test_filterRobotPolicies(t *testing.T) {
 	type args struct {
-		p        *models.Project
+		p        *proModels.Project
 		policies []*types.Policy
 	}
 	tests := []struct {
@@ -161,7 +161,7 @@ func Test_filterRobotPolicies(t *testing.T) {
 		{
 			"policies of one project",
 			args{
-				&models.Project{ProjectID: 1},
+				&proModels.Project{ProjectID: 1},
 				[]*types.Policy{
 					{Resource: "/project/1/repository", Action: "pull", Effect: "allow"},
 				},
@@ -173,7 +173,7 @@ func Test_filterRobotPolicies(t *testing.T) {
 		{
 			"policies of multi projects",
 			args{
-				&models.Project{ProjectID: 1},
+				&proModels.Project{ProjectID: 1},
 				[]*types.Policy{
 					{Resource: "/project/1/repository", Action: "pull", Effect: "allow"},
 					{Resource: "/project/2/repository", Action: "pull", Effect: "allow"},
diff --git a/src/controller/event/handler/internal/util_test.go b/src/controller/event/handler/internal/util_test.go
index dcb24e53e..fb018d140 100644
--- a/src/controller/event/handler/internal/util_test.go
+++ b/src/controller/event/handler/internal/util_test.go
@@ -17,12 +17,12 @@ package internal
 import (
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/controller/scan"
 	"github.com/goharbor/harbor/src/lib/errors"
 	"github.com/goharbor/harbor/src/lib/orm"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
 	scantesting "github.com/goharbor/harbor/src/testing/controller/scan"
 	ormtesting "github.com/goharbor/harbor/src/testing/lib/orm"
@@ -65,9 +65,9 @@ func (suite *AutoScanTestSuite) TestGetProjectFailed() {
 }
 
 func (suite *AutoScanTestSuite) TestAutoScanDisabled() {
-	mock.OnAnything(suite.projectController, "Get").Return(&models.Project{
+	mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{
 		Metadata: map[string]string{
-			models.ProMetaAutoScan: "false",
+			proModels.ProMetaAutoScan: "false",
 		},
 	}, nil)
 
@@ -78,9 +78,9 @@ func (suite *AutoScanTestSuite) TestAutoScanDisabled() {
 }
 
 func (suite *AutoScanTestSuite) TestAutoScan() {
-	mock.OnAnything(suite.projectController, "Get").Return(&models.Project{
+	mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{
 		Metadata: map[string]string{
-			models.ProMetaAutoScan: "true",
+			proModels.ProMetaAutoScan: "true",
 		},
 	}, nil)
 
@@ -93,9 +93,9 @@ func (suite *AutoScanTestSuite) TestAutoScan() {
 }
 
 func (suite *AutoScanTestSuite) TestAutoScanFailed() {
-	mock.OnAnything(suite.projectController, "Get").Return(&models.Project{
+	mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{
 		Metadata: map[string]string{
-			models.ProMetaAutoScan: "true",
+			proModels.ProMetaAutoScan: "true",
 		},
 	}, nil)
 
diff --git a/src/controller/event/handler/webhook/artifact/artifact.go b/src/controller/event/handler/webhook/artifact/artifact.go
index a03c8208d..1249669be 100644
--- a/src/controller/event/handler/webhook/artifact/artifact.go
+++ b/src/controller/event/handler/webhook/artifact/artifact.go
@@ -19,7 +19,6 @@ import (
 	"fmt"
 
 	beegorm "github.com/astaxie/beego/orm"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/controller/event/handler/util"
 	"github.com/goharbor/harbor/src/controller/project"
@@ -28,6 +27,7 @@ import (
 	"github.com/goharbor/harbor/src/pkg/notification"
 	"github.com/goharbor/harbor/src/pkg/notifier/model"
 	notifyModel "github.com/goharbor/harbor/src/pkg/notifier/model"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"github.com/goharbor/harbor/src/pkg/repository"
 )
 
@@ -90,15 +90,15 @@ func (a *Handler) handle(ctx context.Context, event *event.ArtifactEvent) error
 	return nil
 }
 
-func (a *Handler) constructArtifactPayload(event *event.ArtifactEvent, project *models.Project) (*model.Payload, error) {
+func (a *Handler) constructArtifactPayload(event *event.ArtifactEvent, project *proModels.Project) (*model.Payload, error) {
 	repoName := event.Repository
 	if repoName == "" {
 		return nil, fmt.Errorf("invalid %s event with empty repo name", event.EventType)
 	}
 
-	repoType := models.ProjectPrivate
+	repoType := proModels.ProjectPrivate
 	if project.IsPublic() {
-		repoType = models.ProjectPublic
+		repoType = proModels.ProjectPublic
 	}
 
 	imageName := util.GetNameFromImgRepoFullName(repoName)
diff --git a/src/controller/event/handler/webhook/artifact/replication.go b/src/controller/event/handler/webhook/artifact/replication.go
index 87f489a07..c68dbc19b 100644
--- a/src/controller/event/handler/webhook/artifact/replication.go
+++ b/src/controller/event/handler/webhook/artifact/replication.go
@@ -4,11 +4,11 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"strings"
 
 	"github.com/goharbor/harbor/src/lib/config"
 
-	commonModels "github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/controller/event/handler/util"
 	ctlModel "github.com/goharbor/harbor/src/controller/event/model"
@@ -73,7 +73,7 @@ func (r *ReplicationHandler) IsStateful() bool {
 	return false
 }
 
-func constructReplicationPayload(event *event.ReplicationEvent) (*model.Payload, *commonModels.Project, error) {
+func constructReplicationPayload(event *event.ReplicationEvent) (*model.Payload, *proModels.Project, error) {
 	ctx := orm.Context()
 	task, err := replication.Ctl.GetTask(ctx, event.ReplicationTaskID)
 	if err != nil {
diff --git a/src/controller/event/handler/webhook/artifact/replication_test.go b/src/controller/event/handler/webhook/artifact/replication_test.go
index 6c7a0d35c..1d0ec0453 100644
--- a/src/controller/event/handler/webhook/artifact/replication_test.go
+++ b/src/controller/event/handler/webhook/artifact/replication_test.go
@@ -16,13 +16,13 @@ package artifact
 
 import (
 	"context"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"testing"
 	"time"
 
 	"github.com/goharbor/harbor/src/lib/config"
 
 	common_dao "github.com/goharbor/harbor/src/common/dao"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/controller/project"
 	repctl "github.com/goharbor/harbor/src/controller/replication"
@@ -67,7 +67,7 @@ func TestReplicationHandler_Handle(t *testing.T) {
 		},
 	}, nil)
 
-	mock.OnAnything(projectCtl, "GetByName").Return(&models.Project{ProjectID: 1}, nil)
+	mock.OnAnything(projectCtl, "GetByName").Return(&proModels.Project{ProjectID: 1}, nil)
 
 	handler := &ReplicationHandler{}
 
diff --git a/src/controller/event/handler/webhook/chart/chart.go b/src/controller/event/handler/webhook/chart/chart.go
index 3f78f749d..854e7a946 100644
--- a/src/controller/event/handler/webhook/chart/chart.go
+++ b/src/controller/event/handler/webhook/chart/chart.go
@@ -20,13 +20,13 @@ import (
 	"fmt"
 	"github.com/goharbor/harbor/src/lib/config"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/controller/event/handler/util"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/lib/log"
 	"github.com/goharbor/harbor/src/pkg/notification"
 	"github.com/goharbor/harbor/src/pkg/notifier/model"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 )
 
 // Handler preprocess chart event data
@@ -83,10 +83,10 @@ func (cph *Handler) IsStateful() bool {
 	return false
 }
 
-func constructChartPayload(event *event.ChartEvent, project *models.Project) (*model.Payload, error) {
-	repoType := models.ProjectPrivate
+func constructChartPayload(event *event.ChartEvent, project *proModels.Project) (*model.Payload, error) {
+	repoType := proModels.ProjectPrivate
 	if project.IsPublic() {
-		repoType = models.ProjectPublic
+		repoType = proModels.ProjectPublic
 	}
 
 	payload := &model.Payload{
diff --git a/src/controller/event/handler/webhook/chart/chart_test.go b/src/controller/event/handler/webhook/chart/chart_test.go
index e37c35544..e7f53b5f0 100644
--- a/src/controller/event/handler/webhook/chart/chart_test.go
+++ b/src/controller/event/handler/webhook/chart/chart_test.go
@@ -20,10 +20,10 @@ import (
 	"github.com/goharbor/harbor/src/lib/config"
 	_ "github.com/goharbor/harbor/src/pkg/config/db"
 	_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"os"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/pkg/notification"
@@ -57,15 +57,15 @@ func TestChartPreprocessHandler_Handle(t *testing.T) {
 	project.Ctl = projectCtl
 
 	name := "project_for_test_chart_event_preprocess"
-	mock.OnAnything(projectCtl, "Get").Return(func(ctx context.Context, projectIDOrName interface{}, options ...project.Option) *models.Project {
-		return &models.Project{
+	mock.OnAnything(projectCtl, "Get").Return(func(ctx context.Context, projectIDOrName interface{}, options ...project.Option) *proModels.Project {
+		return &proModels.Project{
 			Name:    name,
 			OwnerID: 1,
 			Metadata: map[string]string{
-				models.ProMetaEnableContentTrust:   "true",
-				models.ProMetaPreventVul:           "true",
-				models.ProMetaSeverity:             "Low",
-				models.ProMetaReuseSysCVEAllowlist: "false",
+				proModels.ProMetaEnableContentTrust:   "true",
+				proModels.ProMetaPreventVul:           "true",
+				proModels.ProMetaSeverity:             "Low",
+				proModels.ProMetaReuseSysCVEAllowlist: "false",
 			},
 		}
 	}, nil)
diff --git a/src/controller/event/handler/webhook/quota/quota.go b/src/controller/event/handler/webhook/quota/quota.go
index a724195bd..62797b6db 100644
--- a/src/controller/event/handler/webhook/quota/quota.go
+++ b/src/controller/event/handler/webhook/quota/quota.go
@@ -19,7 +19,6 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/controller/event/handler/util"
 	"github.com/goharbor/harbor/src/controller/project"
@@ -28,6 +27,7 @@ import (
 	"github.com/goharbor/harbor/src/pkg/notification"
 	"github.com/goharbor/harbor/src/pkg/notifier/model"
 	notifyModel "github.com/goharbor/harbor/src/pkg/notifier/model"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 )
 
 // Handler preprocess image event data
@@ -88,9 +88,9 @@ func constructQuotaPayload(event *event.QuotaEvent) (*model.Payload, error) {
 		return nil, fmt.Errorf("invalid %s event with empty repo name", event.EventType)
 	}
 
-	repoType := models.ProjectPrivate
+	repoType := proModels.ProjectPrivate
 	if event.Project.IsPublic() {
-		repoType = models.ProjectPublic
+		repoType = proModels.ProjectPublic
 	}
 
 	imageName := util.GetNameFromImgRepoFullName(repoName)
diff --git a/src/controller/event/handler/webhook/quota/quota_test.go b/src/controller/event/handler/webhook/quota/quota_test.go
index 9bbaf91e2..78a521ce8 100644
--- a/src/controller/event/handler/webhook/quota/quota_test.go
+++ b/src/controller/event/handler/webhook/quota/quota_test.go
@@ -21,12 +21,12 @@ import (
 	"github.com/goharbor/harbor/src/lib/config"
 	_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
 	policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"github.com/goharbor/harbor/src/testing/mock"
 	"testing"
 	"time"
 
 	"github.com/goharbor/harbor/src/common"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/pkg/notification"
 	"github.com/goharbor/harbor/src/pkg/notification/policy"
 	"github.com/goharbor/harbor/src/pkg/notifier"
@@ -65,7 +65,7 @@ func (suite *QuotaPreprocessHandlerSuite) SetupSuite() {
 		OccurAt:   time.Now().UTC(),
 		RepoName:  "hello-world",
 		Resource:  res,
-		Project: &models.Project{
+		Project: &proModels.Project{
 			ProjectID: 1,
 			Name:      "library",
 		},
diff --git a/src/controller/event/handler/webhook/scan/scan.go b/src/controller/event/handler/webhook/scan/scan.go
index a0eba88b0..713adb6b6 100644
--- a/src/controller/event/handler/webhook/scan/scan.go
+++ b/src/controller/event/handler/webhook/scan/scan.go
@@ -19,7 +19,6 @@ import (
 	"time"
 
 	o "github.com/astaxie/beego/orm"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/controller/event/handler/util"
@@ -30,6 +29,7 @@ import (
 	"github.com/goharbor/harbor/src/lib/orm"
 	"github.com/goharbor/harbor/src/pkg/notification"
 	"github.com/goharbor/harbor/src/pkg/notifier/model"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
 )
 
@@ -88,10 +88,10 @@ func (si *Handler) IsStateful() bool {
 	return false
 }
 
-func constructScanImagePayload(event *event.ScanImageEvent, project *models.Project) (*model.Payload, error) {
-	repoType := models.ProjectPrivate
+func constructScanImagePayload(event *event.ScanImageEvent, project *proModels.Project) (*model.Payload, error) {
+	repoType := proModels.ProjectPrivate
 	if project.IsPublic() {
-		repoType = models.ProjectPublic
+		repoType = proModels.ProjectPublic
 	}
 
 	repoName := util.GetNameFromImgRepoFullName(event.Artifact.Repository)
diff --git a/src/controller/event/metadata/quota.go b/src/controller/event/metadata/quota.go
index 2b30c0574..1f490ccbf 100644
--- a/src/controller/event/metadata/quota.go
+++ b/src/controller/event/metadata/quota.go
@@ -1,9 +1,9 @@
 package metadata
 
 import (
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"time"
 
-	"github.com/goharbor/harbor/src/common/models"
 	event2 "github.com/goharbor/harbor/src/controller/event"
 	"github.com/goharbor/harbor/src/lib/errors"
 	"github.com/goharbor/harbor/src/pkg/notifier/event"
@@ -11,7 +11,7 @@ import (
 
 // QuotaMetaData defines quota related event data
 type QuotaMetaData struct {
-	Project  *models.Project
+	Project  *proModels.Project
 	RepoName string
 	Tag      string
 	Digest   string
diff --git a/src/controller/event/topic.go b/src/controller/event/topic.go
index ca3a76127..26e616323 100644
--- a/src/controller/event/topic.go
+++ b/src/controller/event/topic.go
@@ -16,9 +16,9 @@ package event
 
 import (
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"time"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/lib/selector"
 	"github.com/goharbor/harbor/src/pkg/artifact"
 	"github.com/goharbor/harbor/src/pkg/audit/model"
@@ -318,7 +318,7 @@ func (c *ChartEvent) String() string {
 // QuotaEvent is project quota related event data to publish
 type QuotaEvent struct {
 	EventType string
-	Project   *models.Project
+	Project   *proModels.Project
 	Resource  *ImgResource
 	OccurAt   time.Time
 	RepoName  string
diff --git a/src/controller/immutable/controller_test.go b/src/controller/immutable/controller_test.go
index 7670b2277..334629080 100644
--- a/src/controller/immutable/controller_test.go
+++ b/src/controller/immutable/controller_test.go
@@ -3,10 +3,10 @@ package immutable
 import (
 	"github.com/goharbor/harbor/src/lib/orm"
 	"github.com/goharbor/harbor/src/lib/q"
+	"github.com/goharbor/harbor/src/pkg/project"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/dao"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/utils/test"
 	"github.com/goharbor/harbor/src/pkg/immutable/model"
 	htesting "github.com/goharbor/harbor/src/testing"
@@ -37,13 +37,14 @@ func (s *ControllerTestSuite) SetupSuite() {
 func (s *ControllerTestSuite) TestImmutableRule() {
 
 	var err error
+	ctx := s.Context()
 
-	projectID, err := dao.AddProject(models.Project{
-		Name:    "TestImmutableRule",
+	projectID, err := project.Mgr.Create(ctx, &proModels.Project{
+		Name:    "testimmutablerule",
 		OwnerID: 1,
 	})
 	if s.Nil(err) {
-		defer dao.DeleteProject(projectID)
+		defer project.Mgr.Delete(ctx, projectID)
 	}
 
 	rule := &model.Metadata{
diff --git a/src/controller/p2p/preheat/enforcer.go b/src/controller/p2p/preheat/enforcer.go
index b3b18e78a..d58a8e6a4 100644
--- a/src/controller/p2p/preheat/enforcer.go
+++ b/src/controller/p2p/preheat/enforcer.go
@@ -17,10 +17,10 @@ package preheat
 import (
 	"context"
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"strings"
 
 	tk "github.com/docker/distribution/registry/auth/token"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/controller/scan"
@@ -344,7 +344,7 @@ func (de *defaultEnforcer) PreheatArtifact(ctx context.Context, art *artifact.Ar
 }
 
 // getCandidates get the initial candidates by evaluating the policy
-func (de *defaultEnforcer) getCandidates(ctx context.Context, ps *pol.Schema, p *models.Project) ([]*selector.Candidate, error) {
+func (de *defaultEnforcer) getCandidates(ctx context.Context, ps *pol.Schema, p *proModels.Project) ([]*selector.Candidate, error) {
 	// Get the initial candidates
 	// Here we have a hidden filter, the artifact type filter.
 	// Only get the image type at this moment.
@@ -480,7 +480,7 @@ func (de *defaultEnforcer) startTask(ctx context.Context, executionID int64, can
 }
 
 // getVulnerabilitySev gets the severity code value for the given artifact with allowlist option set
-func (de *defaultEnforcer) getVulnerabilitySev(ctx context.Context, p *models.Project, art *artifact.Artifact) (uint, error) {
+func (de *defaultEnforcer) getVulnerabilitySev(ctx context.Context, p *proModels.Project, art *artifact.Artifact) (uint, error) {
 	vulnerable, err := de.scanCtl.GetVulnerable(ctx, art, p.CVEAllowlist.CVESet())
 	if err != nil {
 		if errors.IsNotFoundErr(err) {
@@ -505,7 +505,7 @@ func (de *defaultEnforcer) getVulnerabilitySev(ctx context.Context, p *models.Pr
 }
 
 // toCandidates converts the artifacts to filtering candidates
-func (de *defaultEnforcer) toCandidates(ctx context.Context, p *models.Project, arts []*artifact.Artifact) ([]*selector.Candidate, error) {
+func (de *defaultEnforcer) toCandidates(ctx context.Context, p *proModels.Project, arts []*artifact.Artifact) ([]*selector.Candidate, error) {
 	// Convert to filtering candidates first
 	candidates := make([]*selector.Candidate, 0)
 
@@ -539,7 +539,7 @@ func (de *defaultEnforcer) toCandidates(ctx context.Context, p *models.Project,
 }
 
 // getProject gets the full metadata of the specified project
-func (de *defaultEnforcer) getProject(ctx context.Context, id int64) (*models.Project, error) {
+func (de *defaultEnforcer) getProject(ctx context.Context, id int64) (*proModels.Project, error) {
 	// Get project info with CVE allow list and metadata
 	return de.proCtl.Get(ctx, id, project.WithEffectCVEAllowlist())
 }
@@ -599,7 +599,7 @@ func checkProviderHealthy(inst *provider.Instance) error {
 // Check the project security settings and override the related settings in the policy if necessary.
 // NOTES: if the security settings (relevant with signature and vulnerability) are set at the project configuration,
 // the corresponding filters of P2P preheat policy will be set using the relevant settings of project configurations.
-func overrideSecuritySettings(p *pol.Schema, pro *models.Project) [][]interface{} {
+func overrideSecuritySettings(p *pol.Schema, pro *proModels.Project) [][]interface{} {
 	if p == nil || pro == nil {
 		return nil
 	}
diff --git a/src/controller/p2p/preheat/enforcer_test.go b/src/controller/p2p/preheat/enforcer_test.go
index 7d38742b3..50aee4950 100644
--- a/src/controller/p2p/preheat/enforcer_test.go
+++ b/src/controller/p2p/preheat/enforcer_test.go
@@ -17,12 +17,12 @@ package preheat
 import (
 	"context"
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 	"time"
 
-	"github.com/goharbor/harbor/src/common/models"
 	car "github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/scan"
 	"github.com/goharbor/harbor/src/controller/tag"
@@ -118,7 +118,7 @@ func (suite *EnforcerTestSuite) SetupSuite() {
 		(int64)(1),
 		mock.Anything,
 		mock.Anything,
-	).Return(&models.Project{
+	).Return(&proModels.Project{
 		ProjectID:    1,
 		Name:         "library",
 		CVEAllowlist: models2.CVEAllowlist{},
diff --git a/src/controller/proxy/controller.go b/src/controller/proxy/controller.go
index 6589c5633..0feadb960 100644
--- a/src/controller/proxy/controller.go
+++ b/src/controller/proxy/controller.go
@@ -18,6 +18,7 @@ import (
 	"context"
 	"fmt"
 	"github.com/goharbor/harbor/src/controller/tag"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"io"
 	"strings"
 	"sync"
@@ -25,7 +26,6 @@ import (
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/manifest/manifestlist"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/blob"
 	"github.com/goharbor/harbor/src/controller/event/operator"
@@ -60,7 +60,7 @@ type Controller interface {
 	UseLocalManifest(ctx context.Context, art lib.ArtifactInfo, remote RemoteInterface) (bool, *ManifestList, error)
 	// ProxyBlob proxy the blob request to the remote server, p is the proxy project
 	// art is the ArtifactInfo which includes the digest of the blob
-	ProxyBlob(ctx context.Context, p *models.Project, art lib.ArtifactInfo) (int64, io.ReadCloser, error)
+	ProxyBlob(ctx context.Context, p *proModels.Project, art lib.ArtifactInfo) (int64, io.ReadCloser, error)
 	// ProxyManifest proxy the manifest request to the remote server, p is the proxy project,
 	// art is the ArtifactInfo which includes the tag or digest of the manifest
 	ProxyManifest(ctx context.Context, art lib.ArtifactInfo, remote RemoteInterface) (distribution.Manifest, error)
@@ -232,7 +232,7 @@ func (c *controller) HeadManifest(ctx context.Context, art lib.ArtifactInfo, rem
 	ref := getReference(art)
 	return remote.ManifestExist(remoteRepo, ref)
 }
-func (c *controller) ProxyBlob(ctx context.Context, p *models.Project, art lib.ArtifactInfo) (int64, io.ReadCloser, error) {
+func (c *controller) ProxyBlob(ctx context.Context, p *proModels.Project, art lib.ArtifactInfo) (int64, io.ReadCloser, error) {
 	remoteRepo := getRemoteRepo(art)
 	log.Debugf("The blob doesn't exist, proxy the request to the target server, url:%v", remoteRepo)
 	rHelper, err := NewRemoteHelper(p.RegistryID)
diff --git a/src/controller/proxy/controller_test.go b/src/controller/proxy/controller_test.go
index 85ff442bd..f0156bc7e 100644
--- a/src/controller/proxy/controller_test.go
+++ b/src/controller/proxy/controller_test.go
@@ -17,12 +17,12 @@ package proxy
 import (
 	"context"
 	"github.com/docker/distribution"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/blob"
 	"github.com/goharbor/harbor/src/lib"
 	_ "github.com/goharbor/harbor/src/lib/cache"
 	"github.com/goharbor/harbor/src/lib/errors"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	testproxy "github.com/goharbor/harbor/src/testing/controller/proxy"
 	"github.com/opencontainers/go-digest"
 	"github.com/stretchr/testify/mock"
@@ -83,13 +83,13 @@ type proxyControllerTestSuite struct {
 	local  *localInterfaceMock
 	remote *testproxy.RemoteInterface
 	ctr    Controller
-	proj   *models.Project
+	proj   *proModels.Project
 }
 
 func (p *proxyControllerTestSuite) SetupTest() {
 	p.local = &localInterfaceMock{}
 	p.remote = &testproxy.RemoteInterface{}
-	p.proj = &models.Project{RegistryID: 1}
+	p.proj = &proModels.Project{RegistryID: 1}
 	p.ctr = &controller{
 		blobCtl:     blob.Ctl,
 		artifactCtl: artifact.Ctl,
diff --git a/src/controller/quota/driver/project/project.go b/src/controller/quota/driver/project/project.go
index 53d60dd86..734724c4a 100644
--- a/src/controller/quota/driver/project/project.go
+++ b/src/controller/quota/driver/project/project.go
@@ -19,10 +19,10 @@ import (
 	"fmt"
 	"github.com/goharbor/harbor/src/lib/config"
 	"github.com/goharbor/harbor/src/pkg/config/db"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"strconv"
 
 	"github.com/goharbor/harbor/src/common"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/blob"
 	"github.com/goharbor/harbor/src/lib/log"
 	dr "github.com/goharbor/harbor/src/pkg/quota/driver"
@@ -68,7 +68,7 @@ func (d *driver) Load(ctx context.Context, key string) (dr.RefObject, error) {
 		return nil, err
 	}
 
-	project, ok := result.(*models.Project)
+	project, ok := result.(*proModels.Project)
 	if !ok {
 		return nil, fmt.Errorf("bad result for project: %s", key)
 	}
diff --git a/src/controller/quota/driver/project/util.go b/src/controller/quota/driver/project/util.go
index 55883a69b..7ad7a83f2 100644
--- a/src/controller/quota/driver/project/util.go
+++ b/src/controller/quota/driver/project/util.go
@@ -16,6 +16,7 @@ package project
 
 import (
 	"context"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"strconv"
 
 	"github.com/goharbor/harbor/src/common/models"
@@ -50,7 +51,7 @@ func getProjectsBatchFn(ctx context.Context, keys dataloader.Keys) []*dataloader
 	}
 
 	var ownerIDs []interface{}
-	var projectsMap = make(map[int64]*models.Project, len(projectIDs))
+	var projectsMap = make(map[int64]*proModels.Project, len(projectIDs))
 	for _, project := range projects {
 		ownerIDs = append(ownerIDs, project.OwnerID)
 		projectsMap[project.ProjectID] = project
diff --git a/src/controller/quota/util_test.go b/src/controller/quota/util_test.go
index 31c3ecf45..4b7229720 100644
--- a/src/controller/quota/util_test.go
+++ b/src/controller/quota/util_test.go
@@ -16,11 +16,11 @@ package quota
 
 import (
 	"context"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"math/rand"
 	"testing"
 	"time"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/lib/orm"
 	"github.com/goharbor/harbor/src/lib/q"
@@ -76,21 +76,21 @@ func (suite *RefreshForProjectsTestSuite) TestRefreshForProjects() {
 	rand.Seed(time.Now().UnixNano())
 
 	startProjectID := rand.Int63()
-	var firstPageProjects, secondPageProjects []*models.Project
+	var firstPageProjects, secondPageProjects []*proModels.Project
 	for i := 0; i < 50; i++ {
-		firstPageProjects = append(firstPageProjects, &models.Project{
+		firstPageProjects = append(firstPageProjects, &proModels.Project{
 			ProjectID: startProjectID + int64(i),
 		})
 	}
 
 	for i := 0; i < 10; i++ {
-		secondPageProjects = append(secondPageProjects, &models.Project{
+		secondPageProjects = append(secondPageProjects, &proModels.Project{
 			ProjectID: startProjectID + 50 + int64(i),
 		})
 	}
 
 	page := 1
-	mock.OnAnything(suite.projectCtl, "List").Return(func(context.Context, *q.Query, ...project.Option) []*models.Project {
+	mock.OnAnything(suite.projectCtl, "List").Return(func(context.Context, *q.Query, ...project.Option) []*proModels.Project {
 		defer func() {
 			page++
 		}()
diff --git a/src/controller/repository/controller_test.go b/src/controller/repository/controller_test.go
index 8a31585e5..2a2d4bcff 100644
--- a/src/controller/repository/controller_test.go
+++ b/src/controller/repository/controller_test.go
@@ -15,9 +15,9 @@
 package repository
 
 import (
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/lib/errors"
 	"github.com/goharbor/harbor/src/lib/orm"
@@ -71,7 +71,7 @@ func (c *controllerTestSuite) TestEnsure() {
 
 	// doesn't exist
 	c.repoMgr.On("GetByName", mock.Anything, mock.Anything).Return(nil, errors.NotFoundError(nil))
-	c.proMgr.On("Get", mock.AnythingOfType("*context.valueCtx"), "library").Return(&models.Project{
+	c.proMgr.On("Get", mock.AnythingOfType("*context.valueCtx"), "library").Return(&proModels.Project{
 		ProjectID: 1,
 	}, nil)
 	c.repoMgr.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
diff --git a/src/controller/robot/controller_test.go b/src/controller/robot/controller_test.go
index 7732df1e0..b1ca63bc6 100644
--- a/src/controller/robot/controller_test.go
+++ b/src/controller/robot/controller_test.go
@@ -4,12 +4,12 @@ import (
 	"context"
 	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils"
 	"github.com/goharbor/harbor/src/common"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/utils/test"
 	"github.com/goharbor/harbor/src/lib/config"
 	"github.com/goharbor/harbor/src/lib/q"
 	_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
 	"github.com/goharbor/harbor/src/pkg/permission/types"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	rbac_model "github.com/goharbor/harbor/src/pkg/rbac/model"
 	"github.com/goharbor/harbor/src/pkg/robot/model"
 	htesting "github.com/goharbor/harbor/src/testing"
@@ -33,7 +33,7 @@ func (suite *ControllerTestSuite) TestGet() {
 
 	c := controller{robotMgr: robotMgr, rbacMgr: rbacMgr, proMgr: projectMgr}
 	ctx := context.TODO()
-	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&models.Project{ProjectID: 1, Name: "library"}, nil)
+	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
 	robotMgr.On("Get", mock.Anything, mock.Anything).Return(&model.Robot{
 		Name:        "library+test",
 		Description: "test get method",
@@ -101,7 +101,7 @@ func (suite *ControllerTestSuite) TestCreate() {
 
 	c := controller{robotMgr: robotMgr, rbacMgr: rbacMgr, proMgr: projectMgr}
 	ctx := context.TODO()
-	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&models.Project{ProjectID: 1, Name: "library"}, nil)
+	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
 	robotMgr.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
 	rbacMgr.On("CreateRbacPolicy", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
 	rbacMgr.On("CreatePermission", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
@@ -164,7 +164,7 @@ func (suite *ControllerTestSuite) TestUpdate() {
 	config.InitWithSettings(conf)
 
 	robotMgr.On("Update", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
-	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&models.Project{ProjectID: 1, Name: "library"}, nil)
+	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
 	rbacMgr.On("DeletePermissionsByRole", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 
 	rbacMgr.On("CreateRbacPolicy", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
@@ -208,7 +208,7 @@ func (suite *ControllerTestSuite) TestList() {
 	c := controller{robotMgr: robotMgr, rbacMgr: rbacMgr, proMgr: projectMgr}
 	ctx := context.TODO()
 
-	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&models.Project{ProjectID: 1, Name: "library"}, nil)
+	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
 	robotMgr.On("List", mock.Anything, mock.Anything).Return([]*model.Robot{
 		{
 			Name:        "test",
@@ -233,7 +233,7 @@ func (suite *ControllerTestSuite) TestList() {
 			Action:   "push",
 		},
 	}, nil)
-	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&models.Project{ProjectID: 1, Name: "library"}, nil)
+	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
 	rs, err := c.List(ctx, &q.Query{
 		Keywords: map[string]interface{}{
 			"name": "test3",
@@ -257,7 +257,7 @@ func (suite *ControllerTestSuite) TestToScope() {
 	c := controller{robotMgr: robotMgr, rbacMgr: rbacMgr, proMgr: projectMgr}
 	ctx := context.TODO()
 
-	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&models.Project{ProjectID: 1, Name: "library"}, nil)
+	projectMgr.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
 
 	p := &Permission{
 		Kind:      "system",
diff --git a/src/core/api/chart_label.go b/src/core/api/chart_label.go
index f7790bb48..c29116bd4 100644
--- a/src/core/api/chart_label.go
+++ b/src/core/api/chart_label.go
@@ -3,6 +3,7 @@ package api
 import (
 	"errors"
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 
 	"github.com/goharbor/harbor/src/common"
 	"github.com/goharbor/harbor/src/common/models"
@@ -18,7 +19,7 @@ const (
 // ChartLabelAPI handles the requests of marking/removing labels to/from charts.
 type ChartLabelAPI struct {
 	LabelResourceAPI
-	project       *models.Project
+	project       *proModels.Project
 	chartFullName string
 }
 
diff --git a/src/core/api/chart_repository_test.go b/src/core/api/chart_repository_test.go
index 6b3656e36..5e8f3a7e6 100644
--- a/src/core/api/chart_repository_test.go
+++ b/src/core/api/chart_repository_test.go
@@ -3,13 +3,13 @@ package api
 import (
 	"context"
 	"errors"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
 	bcontext "github.com/astaxie/beego/context"
 	"github.com/goharbor/harbor/src/chartserver"
-	"github.com/goharbor/harbor/src/common/models"
 	projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
 	"github.com/goharbor/harbor/src/testing/mock"
 )
@@ -42,7 +42,7 @@ func TestRequireNamespace(t *testing.T) {
 	projectCtl := &projecttesting.Controller{}
 	chartAPI.ProjectCtl = projectCtl
 
-	mock.OnAnything(projectCtl, "List").Return([]*models.Project{
+	mock.OnAnything(projectCtl, "List").Return([]*proModels.Project{
 		{ProjectID: 0, Name: "library"},
 		{ProjectID: 1, Name: "repo2"},
 	}, nil)
diff --git a/src/core/api/harborapi_test.go b/src/core/api/harborapi_test.go
index 99650dd4a..ed67e8577 100644
--- a/src/core/api/harborapi_test.go
+++ b/src/core/api/harborapi_test.go
@@ -19,6 +19,21 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"github.com/astaxie/beego"
+	"github.com/dghubble/sling"
+	"github.com/goharbor/harbor/src/common/api"
+	"github.com/goharbor/harbor/src/common/dao"
+	"github.com/goharbor/harbor/src/common/job/test"
+	testutils "github.com/goharbor/harbor/src/common/utils/test"
+	_ "github.com/goharbor/harbor/src/core/auth/db"
+	_ "github.com/goharbor/harbor/src/core/auth/ldap"
+	"github.com/goharbor/harbor/src/lib/config"
+	libOrm "github.com/goharbor/harbor/src/lib/orm"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
+	"github.com/goharbor/harbor/src/server/middleware"
+	"github.com/goharbor/harbor/src/server/middleware/orm"
+	"github.com/goharbor/harbor/src/server/middleware/security"
+	"github.com/goharbor/harbor/src/testing/apitests/apilib"
 	"io/ioutil"
 	"log"
 	"net/http"
@@ -26,23 +41,6 @@ import (
 	"path/filepath"
 	"runtime"
 	"strconv"
-	"strings"
-
-	"github.com/astaxie/beego"
-	"github.com/dghubble/sling"
-	"github.com/goharbor/harbor/src/common/api"
-	"github.com/goharbor/harbor/src/common/dao"
-	"github.com/goharbor/harbor/src/common/job/test"
-	"github.com/goharbor/harbor/src/common/models"
-	testutils "github.com/goharbor/harbor/src/common/utils/test"
-	_ "github.com/goharbor/harbor/src/core/auth/db"
-	_ "github.com/goharbor/harbor/src/core/auth/ldap"
-	"github.com/goharbor/harbor/src/lib/config"
-	libOrm "github.com/goharbor/harbor/src/lib/orm"
-	"github.com/goharbor/harbor/src/server/middleware"
-	"github.com/goharbor/harbor/src/server/middleware/orm"
-	"github.com/goharbor/harbor/src/server/middleware/security"
-	"github.com/goharbor/harbor/src/testing/apitests/apilib"
 )
 
 const (
@@ -298,7 +296,7 @@ func (a testapi) ProjectsGet(query *apilib.ProjectQuery, authInfo ...usrInfo) (i
 
 // Update properties for a selected project.
 func (a testapi) ProjectsPut(prjUsr usrInfo, projectID string,
-	project *models.Project) (int, error) {
+	project *proModels.Project) (int, error) {
 	path := "/api/projects/" + projectID
 	_sling := sling.New().Put(a.basePath).Path(path).BodyJSON(project)
 
@@ -367,31 +365,6 @@ func (a testapi) GetProjectMembersByProID(prjUsr usrInfo, projectID string) (int
 	return httpStatusCode, successPayload, err
 }
 
-// Add project role member accompany with  projectID
-// func (a testapi) AddProjectMember(prjUsr usrInfo, projectID string, roles apilib.RoleParam) (int, int, error) {
-func (a testapi) AddProjectMember(prjUsr usrInfo, projectID string, member *models.MemberReq) (int, int, error) {
-	_sling := sling.New().Post(a.basePath)
-
-	path := "/api/projects/" + projectID + "/members/"
-	_sling = _sling.Path(path)
-	_sling = _sling.BodyJSON(member)
-	httpStatusCode, header, _, err := request0(_sling, jsonAcceptHeader, prjUsr)
-
-	var memberID int
-	location := header.Get("Location")
-	if location != "" {
-		parts := strings.Split(location, "/")
-		if len(parts) > 0 {
-			i, err := strconv.Atoi(parts[len(parts)-1])
-			if err == nil {
-				memberID = i
-			}
-		}
-	}
-
-	return httpStatusCode, memberID, err
-}
-
 // Delete project role member accompany with  projectID
 func (a testapi) DeleteProjectMember(authInfo usrInfo, projectID string, memberID string) (int, error) {
 	_sling := sling.New().Delete(a.basePath)
diff --git a/src/core/auth/ldap/ldap_test.go b/src/core/auth/ldap/ldap_test.go
index 3691ef084..53069f0ee 100644
--- a/src/core/auth/ldap/ldap_test.go
+++ b/src/core/auth/ldap/ldap_test.go
@@ -14,6 +14,7 @@
 package ldap
 
 import (
+	"github.com/goharbor/harbor/src/pkg/project"
 	"os"
 	"testing"
 
@@ -358,7 +359,7 @@ func TestSearchAndOnBoardUser(t *testing.T) {
 func TestAddProjectMemberWithLdapUser(t *testing.T) {
 	memberMgr := member.Mgr
 	ctx := orm.Context()
-	currentProject, err := dao.GetProjectByName("member_test_01")
+	currentProject, err := project.Mgr.Get(ctx, "member_test_01")
 	if err != nil {
 		t.Errorf("Error occurred when GetProjectByName: %v", err)
 	}
@@ -378,7 +379,7 @@ func TestAddProjectMemberWithLdapUser(t *testing.T) {
 		t.Errorf("Error occurred in AddOrUpdateProjectMember: pmid:%v", pmid)
 	}
 
-	currentProject, err = dao.GetProjectByName("member_test_02")
+	currentProject, err = project.Mgr.Get(ctx, "member_test_02")
 	if err != nil {
 		t.Errorf("Error occurred when GetProjectByName: %v", err)
 	}
@@ -400,7 +401,7 @@ func TestAddProjectMemberWithLdapUser(t *testing.T) {
 func TestAddProjectMemberWithLdapGroup(t *testing.T) {
 	memberMgr := member.Mgr
 	ctx := orm.Context()
-	currentProject, err := dao.GetProjectByName("member_test_01")
+	currentProject, err := project.Mgr.Get(ctx, "member_test_01")
 	if err != nil {
 		t.Errorf("Error occurred when GetProjectByName: %v", err)
 	}
diff --git a/src/core/service/token/token_test.go b/src/core/service/token/token_test.go
index eb618975d..cce32793c 100644
--- a/src/core/service/token/token_test.go
+++ b/src/core/service/token/token_test.go
@@ -25,6 +25,7 @@ import (
 	"github.com/goharbor/harbor/src/lib/orm"
 	_ "github.com/goharbor/harbor/src/pkg/config/db"
 	_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"io/ioutil"
 	"net/url"
 	"os"
@@ -34,7 +35,6 @@ import (
 
 	jwt "github.com/dgrijalva/jwt-go"
 	"github.com/docker/distribution/registry/auth/token"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/rbac"
 	"github.com/goharbor/harbor/src/common/security"
 	"github.com/stretchr/testify/assert"
@@ -247,7 +247,7 @@ func (f *fakeSecurityContext) Can(ctx context.Context, action rbac.Action, resou
 	return false
 }
 
-func (f *fakeSecurityContext) GetMyProjects() ([]*models.Project, error) {
+func (f *fakeSecurityContext) GetMyProjects() ([]*proModels.Project, error) {
 	return nil, nil
 }
 func (f *fakeSecurityContext) GetProjectRoles(interface{}) []int {
diff --git a/src/jobservice/job/impl/gc/garbage_collection_test.go b/src/jobservice/job/impl/gc/garbage_collection_test.go
index fd80272b9..77e7d53f0 100644
--- a/src/jobservice/job/impl/gc/garbage_collection_test.go
+++ b/src/jobservice/job/impl/gc/garbage_collection_test.go
@@ -15,11 +15,11 @@
 package gc
 
 import (
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"os"
 	"testing"
 
 	"github.com/docker/distribution/manifest/schema2"
-	"github.com/goharbor/harbor/src/common/models"
 	commom_regctl "github.com/goharbor/harbor/src/common/registryctl"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/jobservice/job"
@@ -117,7 +117,7 @@ func (suite *gcTestSuite) TestRemoveUntaggedBlobs() {
 	logger := &mockjobservice.MockJobLogger{}
 	ctx.On("GetLogger").Return(logger)
 
-	mock.OnAnything(suite.projectCtl, "List").Return([]*models.Project{
+	mock.OnAnything(suite.projectCtl, "List").Return([]*proModels.Project{
 		{
 			ProjectID: 1234,
 			Name:      "test GC",
@@ -226,7 +226,7 @@ func (suite *gcTestSuite) TestRun() {
 	suite.artifactCtl.On("Delete").Return(nil)
 	suite.artrashMgr.On("Filter").Return([]model.ArtifactTrash{}, nil)
 
-	mock.OnAnything(suite.projectCtl, "List").Return([]*models.Project{
+	mock.OnAnything(suite.projectCtl, "List").Return([]*proModels.Project{
 		{
 			ProjectID: 12345,
 			Name:      "test GC",
@@ -301,7 +301,7 @@ func (suite *gcTestSuite) TestMark() {
 		},
 	}, nil)
 
-	mock.OnAnything(suite.projectCtl, "List").Return([]*models.Project{
+	mock.OnAnything(suite.projectCtl, "List").Return([]*proModels.Project{
 		{
 			ProjectID: 1234,
 			Name:      "test GC",
diff --git a/src/pkg/exporter/project_collector_test.go b/src/pkg/exporter/project_collector_test.go
index 67c278bd5..2d297db3d 100644
--- a/src/pkg/exporter/project_collector_test.go
+++ b/src/pkg/exporter/project_collector_test.go
@@ -1,6 +1,7 @@
 package exporter
 
 import (
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"strconv"
 	"testing"
 	"time"
@@ -27,8 +28,8 @@ var (
 	alice    = models.User{Username: "alice", Password: "password", Email: "alice@test.com"}
 	bob      = models.User{Username: "bob", Password: "password", Email: "bob@test.com"}
 	eve      = models.User{Username: "eve", Password: "password", Email: "eve@test.com"}
-	testPro1 = models.Project{OwnerID: 1, Name: "test1", Metadata: map[string]string{"public": "true"}}
-	testPro2 = models.Project{OwnerID: 1, Name: "test2", Metadata: map[string]string{"public": "false"}}
+	testPro1 = proModels.Project{OwnerID: 1, Name: "test1", Metadata: map[string]string{"public": "true"}}
+	testPro2 = proModels.Project{OwnerID: 1, Name: "test2", Metadata: map[string]string{"public": "false"}}
 	rs1      = qtypes.ResourceList{qtypes.ResourceStorage: 100}
 	rs2      = qtypes.ResourceList{qtypes.ResourceStorage: 200}
 	repo1    = model.RepoRecord{Name: "repo1"}
diff --git a/src/pkg/project/dao/dao.go b/src/pkg/project/dao/dao.go
index 420f4e77f..68339240b 100644
--- a/src/pkg/project/dao/dao.go
+++ b/src/pkg/project/dao/dao.go
@@ -70,7 +70,7 @@ func (d *dao) Create(ctx context.Context, project *models.Project) (int64, error
 			return orm.WrapConflictError(err, "The project named %s already exists", project.Name)
 		}
 
-		member := &Member{
+		member := &models.Member{
 			ProjectID:    projectID,
 			EntityID:     project.OwnerID,
 			Role:         common.RoleProjectAdmin,
@@ -170,7 +170,7 @@ func (d *dao) List(ctx context.Context, query *q.Query) ([]*models.Project, erro
 }
 
 func (d *dao) ListRoles(ctx context.Context, projectID int64, userID int, groupIDs ...int) ([]int, error) {
-	qs, err := orm.QuerySetter(ctx, &Member{}, nil)
+	qs, err := orm.QuerySetter(ctx, &models.Member{}, nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/src/pkg/project/dao/model.go b/src/pkg/project/models/member.go
similarity index 80%
rename from src/pkg/project/dao/model.go
rename to src/pkg/project/models/member.go
index c6ee3d491..70f002c0c 100644
--- a/src/pkg/project/dao/model.go
+++ b/src/pkg/project/models/member.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package dao
+package models
 
 import (
 	"time"
@@ -37,6 +37,16 @@ type Member struct {
 	UpdateTime   time.Time `orm:"column(update_time);auto_now" json:"update_time"`
 }
 
+// MemberQuery ...
+type MemberQuery struct {
+	UserID   int    // the user id
+	Name     string // the username of member
+	Role     int    // the role of the member has to the project
+	GroupIDs []int  // the group ID of current user belongs to
+
+	WithPublic bool // include the public projects for the member
+}
+
 // TableName ...
 func (*Member) TableName() string {
 	return "project_member"
diff --git a/src/common/models/pro_meta.go b/src/pkg/project/models/pro_meta.go
similarity index 100%
rename from src/common/models/pro_meta.go
rename to src/pkg/project/models/pro_meta.go
diff --git a/src/pkg/project/models/project.go b/src/pkg/project/models/project.go
index d3a9146d4..3cfa42493 100644
--- a/src/pkg/project/models/project.go
+++ b/src/pkg/project/models/project.go
@@ -15,14 +15,192 @@
 package models
 
 import (
-	"github.com/goharbor/harbor/src/common/models"
+	"context"
+	"fmt"
+	"github.com/astaxie/beego/orm"
+	allowlist "github.com/goharbor/harbor/src/pkg/allowlist/models"
+	"github.com/lib/pq"
+	"strconv"
+	"strings"
+	"time"
 )
 
-// Project ...
-type Project = models.Project
+const (
+	// ProjectTable is the table name for project
+	ProjectTable = "project"
+	// ProjectPublic means project is public
+	ProjectPublic = "public"
+	// ProjectPrivate means project is private
+	ProjectPrivate = "private"
+)
+
+func init() {
+	orm.RegisterModel(&Project{})
+}
+
+// Project holds the details of a project.
+type Project struct {
+	ProjectID    int64                  `orm:"pk;auto;column(project_id)" json:"project_id"`
+	OwnerID      int                    `orm:"column(owner_id)" json:"owner_id"`
+	Name         string                 `orm:"column(name)" json:"name" sort:"default"`
+	CreationTime time.Time              `orm:"column(creation_time);auto_now_add" json:"creation_time"`
+	UpdateTime   time.Time              `orm:"column(update_time);auto_now" json:"update_time"`
+	Deleted      bool                   `orm:"column(deleted)" json:"deleted"`
+	OwnerName    string                 `orm:"-" json:"owner_name"`
+	Role         int                    `orm:"-" json:"current_user_role_id"`
+	RoleList     []int                  `orm:"-" json:"current_user_role_ids"`
+	RepoCount    int64                  `orm:"-" json:"repo_count"`
+	ChartCount   uint64                 `orm:"-" json:"chart_count"`
+	Metadata     map[string]string      `orm:"-" json:"metadata"`
+	CVEAllowlist allowlist.CVEAllowlist `orm:"-" json:"cve_allowlist"`
+	RegistryID   int64                  `orm:"column(registry_id)" json:"registry_id"`
+}
+
+// GetMetadata ...
+func (p *Project) GetMetadata(key string) (string, bool) {
+	if len(p.Metadata) == 0 {
+		return "", false
+	}
+	value, exist := p.Metadata[key]
+	return value, exist
+}
+
+// SetMetadata ...
+func (p *Project) SetMetadata(key, value string) {
+	if p.Metadata == nil {
+		p.Metadata = map[string]string{}
+	}
+	p.Metadata[key] = value
+}
+
+// IsPublic ...
+func (p *Project) IsPublic() bool {
+	public, exist := p.GetMetadata(ProMetaPublic)
+	if !exist {
+		return false
+	}
+
+	return isTrue(public)
+}
+
+// IsProxy returns true when the project type is proxy cache
+func (p *Project) IsProxy() bool {
+	return p.RegistryID > 0
+}
+
+// ContentTrustEnabled ...
+func (p *Project) ContentTrustEnabled() bool {
+	enabled, exist := p.GetMetadata(ProMetaEnableContentTrust)
+	if !exist {
+		return false
+	}
+	return isTrue(enabled)
+}
+
+// VulPrevented ...
+func (p *Project) VulPrevented() bool {
+	prevent, exist := p.GetMetadata(ProMetaPreventVul)
+	if !exist {
+		return false
+	}
+	return isTrue(prevent)
+}
+
+// ReuseSysCVEAllowlist ...
+func (p *Project) ReuseSysCVEAllowlist() bool {
+	r, ok := p.GetMetadata(ProMetaReuseSysCVEAllowlist)
+	if !ok {
+		return true
+	}
+	return isTrue(r)
+}
+
+// Severity ...
+func (p *Project) Severity() string {
+	severity, exist := p.GetMetadata(ProMetaSeverity)
+	if !exist {
+		return ""
+	}
+	return severity
+}
+
+// AutoScan ...
+func (p *Project) AutoScan() bool {
+	auto, exist := p.GetMetadata(ProMetaAutoScan)
+	if !exist {
+		return false
+	}
+	return isTrue(auto)
+}
+
+// FilterByPublic returns orm.QuerySeter with public filter
+func (p *Project) FilterByPublic(ctx context.Context, qs orm.QuerySeter, key string, value interface{}) orm.QuerySeter {
+	subQuery := `SELECT project_id FROM project_metadata WHERE name = 'public' AND value = '%s'`
+	if isTrue(value) {
+		subQuery = fmt.Sprintf(subQuery, "true")
+	} else {
+		subQuery = fmt.Sprintf(subQuery, "false")
+	}
+	return qs.FilterRaw("project_id", fmt.Sprintf("IN (%s)", subQuery))
+}
+
+// FilterByOwner returns orm.QuerySeter with owner filter
+func (p *Project) FilterByOwner(ctx context.Context, qs orm.QuerySeter, key string, value interface{}) orm.QuerySeter {
+	username, ok := value.(string)
+	if !ok {
+		return qs
+	}
+
+	return qs.FilterRaw("owner_id", fmt.Sprintf("IN (SELECT user_id FROM harbor_user WHERE username = %s)", pq.QuoteLiteral(username)))
+}
+
+// FilterByMember returns orm.QuerySeter with member filter
+func (p *Project) FilterByMember(ctx context.Context, qs orm.QuerySeter, key string, value interface{}) orm.QuerySeter {
+	query, ok := value.(*MemberQuery)
+	if !ok {
+		return qs
+	}
+	subQuery := fmt.Sprintf(`SELECT project_id FROM project_member WHERE entity_id = %d AND entity_type = 'u'`, query.UserID)
+	if query.Role > 0 {
+		subQuery = fmt.Sprintf("%s AND role = %d", subQuery, query.Role)
+	}
+
+	if query.WithPublic {
+		subQuery = fmt.Sprintf("(%s) UNION (SELECT project_id FROM project_metadata WHERE name = 'public' AND value = 'true')", subQuery)
+	}
+
+	if len(query.GroupIDs) > 0 {
+		var elems []string
+		for _, groupID := range query.GroupIDs {
+			elems = append(elems, strconv.Itoa(groupID))
+		}
+
+		tpl := "(%s) UNION (SELECT project_id FROM project_member pm, user_group ug WHERE pm.entity_id = ug.id AND pm.entity_type = 'g' AND ug.id IN (%s))"
+		subQuery = fmt.Sprintf(tpl, subQuery, strings.TrimSpace(strings.Join(elems, ", ")))
+	}
+
+	return qs.FilterRaw("project_id", fmt.Sprintf("IN (%s)", subQuery))
+}
+
+func isTrue(i interface{}) bool {
+	switch value := i.(type) {
+	case bool:
+		return value
+	case string:
+		v := strings.ToLower(value)
+		return v == "true" || v == "1"
+	default:
+		return false
+	}
+}
+
+// TableName is required by beego orm to map Project to table project
+func (p *Project) TableName() string {
+	return ProjectTable
+}
 
 // Projects the connection for Project
-type Projects []*models.Project
+type Projects []*Project
 
 // OwnerIDs returns all the owner ids from the projects
 func (projects Projects) OwnerIDs() []int {
@@ -32,9 +210,3 @@ func (projects Projects) OwnerIDs() []int {
 	}
 	return ownerIDs
 }
-
-// Member ...
-type Member = models.Member
-
-// MemberQuery ...
-type MemberQuery = models.MemberQuery
diff --git a/src/pkg/retention/launcher_test.go b/src/pkg/retention/launcher_test.go
index 7540aa2ee..36758f72c 100644
--- a/src/pkg/retention/launcher_test.go
+++ b/src/pkg/retention/launcher_test.go
@@ -17,10 +17,10 @@ package retention
 import (
 	"context"
 	"github.com/goharbor/harbor/src/lib/orm"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"testing"
 
 	"github.com/goharbor/harbor/src/common/job"
-	"github.com/goharbor/harbor/src/common/models"
 	_ "github.com/goharbor/harbor/src/lib/selector/selectors/doublestar"
 	"github.com/goharbor/harbor/src/pkg/project"
 	"github.com/goharbor/harbor/src/pkg/repository/model"
@@ -116,16 +116,16 @@ type launchTestSuite struct {
 }
 
 func (l *launchTestSuite) SetupTest() {
-	pro1 := &models.Project{
+	pro1 := &proModels.Project{
 		ProjectID: 1,
 		Name:      "library",
 	}
-	pro2 := &models.Project{
+	pro2 := &proModels.Project{
 		ProjectID: 2,
 		Name:      "test",
 	}
 	projectMgr := &projecttesting.Manager{}
-	mock.OnAnything(projectMgr, "List").Return([]*models.Project{
+	mock.OnAnything(projectMgr, "List").Return([]*proModels.Project{
 		pro1, pro2,
 	}, nil)
 	l.projectMgr = projectMgr
diff --git a/src/server/middleware/contenttrust/contenttrust_test.go b/src/server/middleware/contenttrust/contenttrust_test.go
index 637642428..9e87adea3 100644
--- a/src/server/middleware/contenttrust/contenttrust_test.go
+++ b/src/server/middleware/contenttrust/contenttrust_test.go
@@ -16,11 +16,11 @@ package contenttrust
 
 import (
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/security"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/artifact/processor/image"
@@ -43,7 +43,7 @@ type MiddlewareTestSuite struct {
 	projectController         *projecttesting.Controller
 
 	artifact *artifact.Artifact
-	project  *models.Project
+	project  *proModels.Project
 
 	isArtifactSigned func(req *http.Request, art lib.ArtifactInfo) (bool, error)
 	next             http.Handler
@@ -65,11 +65,11 @@ func (suite *MiddlewareTestSuite) SetupTest() {
 	suite.artifact.RepositoryName = "library/photon"
 	suite.artifact.Digest = "digest"
 
-	suite.project = &models.Project{
+	suite.project = &proModels.Project{
 		ProjectID: suite.artifact.ProjectID,
 		Name:      "library",
 		Metadata: map[string]string{
-			models.ProMetaEnableContentTrust: "true",
+			proModels.ProMetaEnableContentTrust: "true",
 		},
 	}
 
@@ -121,7 +121,7 @@ func (suite *MiddlewareTestSuite) TestGetProjectFailed() {
 
 func (suite *MiddlewareTestSuite) TestContentTrustDisabled() {
 	mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
-	suite.project.Metadata[models.ProMetaEnableContentTrust] = "false"
+	suite.project.Metadata[proModels.ProMetaEnableContentTrust] = "false"
 	mock.OnAnything(suite.projectController, "GetByName").Return(suite.project, nil)
 
 	req := suite.makeRequest()
diff --git a/src/server/middleware/immutable/pushmf_test.go b/src/server/middleware/immutable/pushmf_test.go
index e5e40a601..82788dcb5 100644
--- a/src/server/middleware/immutable/pushmf_test.go
+++ b/src/server/middleware/immutable/pushmf_test.go
@@ -4,6 +4,8 @@ import (
 	"context"
 	"fmt"
 	"github.com/goharbor/harbor/src/controller/immutable"
+	"github.com/goharbor/harbor/src/pkg/project"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"math/rand"
 	"net/http"
 	"net/http/httptest"
@@ -12,7 +14,6 @@ import (
 	"time"
 
 	"github.com/goharbor/harbor/src/common/dao"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/lib"
 	internal_orm "github.com/goharbor/harbor/src/lib/orm"
 	"github.com/goharbor/harbor/src/pkg/artifact"
@@ -71,8 +72,8 @@ func randomString(n int) string {
 	return string(b)
 }
 
-func (suite *HandlerSuite) addProject(projectName string) int64 {
-	projectID, err := dao.AddProject(models.Project{
+func (suite *HandlerSuite) addProject(ctx context.Context, projectName string) int64 {
+	projectID, err := project.Mgr.Create(ctx, &proModels.Project{
 		Name:    projectName,
 		OwnerID: 1,
 	})
@@ -153,14 +154,14 @@ func (suite *HandlerSuite) TestPutDeleteManifestCreated() {
 	dgt := digest.FromString(randomString(15)).String()
 	ctx := internal_orm.NewContext(context.TODO(), dao.GetOrmer())
 
-	projectID := suite.addProject(projectName)
+	projectID := suite.addProject(ctx, projectName)
 	immuRuleID := suite.addImmutableRule(projectID)
 	repoID := suite.addRepo(ctx, projectID, repoName)
 	afID := suite.addArt(ctx, projectID, repoID, repoName, dgt)
 	tagID := suite.addTags(ctx, repoID, afID, "release-1.10")
 
 	defer func() {
-		dao.DeleteProject(projectID)
+		project.Mgr.Delete(ctx, projectID)
 		artifact.Mgr.Delete(ctx, afID)
 		repository.Mgr.Delete(ctx, repoID)
 		tag.Mgr.Delete(ctx, tagID)
diff --git a/src/server/middleware/quota/copy_artifact_test.go b/src/server/middleware/quota/copy_artifact_test.go
index 8ce720422..352d1696a 100644
--- a/src/server/middleware/quota/copy_artifact_test.go
+++ b/src/server/middleware/quota/copy_artifact_test.go
@@ -29,11 +29,11 @@
 package quota
 
 import (
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
-	commonmodels "github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/pkg/notification"
 	"github.com/goharbor/harbor/src/pkg/quota"
@@ -84,7 +84,7 @@ func (suite *CopyArtifactMiddlewareTestSuite) SetupTest() {
 		walkFn(suite.artifact)
 	})
 
-	mock.OnAnything(suite.projectController, "Get").Return(&commonmodels.Project{}, nil)
+	mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{}, nil)
 }
 
 func (suite *CopyArtifactMiddlewareTestSuite) TestResourcesWarning() {
diff --git a/src/server/middleware/quota/put_blob_upload_test.go b/src/server/middleware/quota/put_blob_upload_test.go
index b700935d2..914044c89 100644
--- a/src/server/middleware/quota/put_blob_upload_test.go
+++ b/src/server/middleware/quota/put_blob_upload_test.go
@@ -16,12 +16,12 @@ package quota
 
 import (
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"strconv"
 	"testing"
 
-	commonmodels "github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/lib/errors"
 	"github.com/goharbor/harbor/src/pkg/notification"
 	"github.com/goharbor/harbor/src/pkg/quota"
@@ -118,7 +118,7 @@ func (suite *PutBlobUploadMiddlewareTestSuite) TestBlobExistFailed() {
 func (suite *PutBlobUploadMiddlewareTestSuite) TestResourcesExceeded() {
 	mock.OnAnything(suite.quotaController, "IsEnabled").Return(true, nil)
 	mock.OnAnything(suite.blobController, "Exist").Return(false, nil)
-	mock.OnAnything(suite.projectController, "Get").Return(&commonmodels.Project{}, nil)
+	mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{}, nil)
 
 	{
 		var errs quota.Errors
diff --git a/src/server/middleware/quota/put_manifest_test.go b/src/server/middleware/quota/put_manifest_test.go
index d7198f3be..b5001628e 100644
--- a/src/server/middleware/quota/put_manifest_test.go
+++ b/src/server/middleware/quota/put_manifest_test.go
@@ -17,13 +17,13 @@ package quota
 import (
 	"context"
 	std_err "errors"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"strings"
 	"testing"
 
 	"github.com/docker/distribution/manifest/schema2"
-	commonmodels "github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/lib/errors"
 	"github.com/goharbor/harbor/src/pkg/blob/models"
 	"github.com/goharbor/harbor/src/pkg/distribution"
@@ -187,7 +187,7 @@ func (suite *PutManifestMiddlewareTestSuite) TestResourcesExceeded() {
 	mock.OnAnything(suite.quotaController, "IsEnabled").Return(true, nil)
 	mock.OnAnything(suite.blobController, "Exist").Return(false, nil)
 	mock.OnAnything(suite.blobController, "FindMissingAssociationsForProject").Return(nil, nil)
-	mock.OnAnything(suite.projectController, "Get").Return(&commonmodels.Project{}, nil)
+	mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{}, nil)
 
 	next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(http.StatusOK)
@@ -235,7 +235,7 @@ func (suite *PutManifestMiddlewareTestSuite) TestResourcesWarning() {
 		f := args.Get(4).(func() error)
 		f()
 	})
-	mock.OnAnything(suite.projectController, "Get").Return(&commonmodels.Project{}, nil)
+	mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{}, nil)
 
 	next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(http.StatusOK)
diff --git a/src/server/middleware/quota/quota_test.go b/src/server/middleware/quota/quota_test.go
index af8852087..e81ab74ae 100644
--- a/src/server/middleware/quota/quota_test.go
+++ b/src/server/middleware/quota/quota_test.go
@@ -16,11 +16,11 @@ package quota
 
 import (
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/blob"
 	"github.com/goharbor/harbor/src/controller/project"
@@ -64,7 +64,7 @@ func (suite *RequestMiddlewareTestSuite) SetupTest() {
 	suite.projectController = &projecttesting.Controller{}
 	projectController = suite.projectController
 
-	mock.OnAnything(suite.projectController, "GetByName").Return(&models.Project{ProjectID: 1, Name: "library"}, nil)
+	mock.OnAnything(suite.projectController, "GetByName").Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
 
 	suite.originallQuotaController = quotaController
 	suite.quotaController = &quotatesting.Controller{}
diff --git a/src/server/middleware/quota/util_test.go b/src/server/middleware/quota/util_test.go
index 20a8f3679..879a2ff2f 100644
--- a/src/server/middleware/quota/util_test.go
+++ b/src/server/middleware/quota/util_test.go
@@ -17,18 +17,18 @@ package quota
 import (
 	"context"
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/testing/controller/project"
 	"github.com/stretchr/testify/mock"
 )
 
 func Test_projectReferenceObject(t *testing.T) {
 	ctl := &project.Controller{}
-	ctl.On("GetByName", mock.AnythingOfType(""), "library").Return(&models.Project{ProjectID: 1}, nil)
+	ctl.On("GetByName", mock.AnythingOfType(""), "library").Return(&proModels.Project{ProjectID: 1}, nil)
 	ctl.On("GetByName", mock.AnythingOfType(""), "demo").Return(nil, fmt.Errorf("not found"))
 
 	originalProjectController := projectController
diff --git a/src/server/middleware/repoproxy/proxy.go b/src/server/middleware/repoproxy/proxy.go
index efc76271f..d121921d3 100644
--- a/src/server/middleware/repoproxy/proxy.go
+++ b/src/server/middleware/repoproxy/proxy.go
@@ -17,7 +17,6 @@ package repoproxy
 import (
 	"context"
 	"fmt"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/security"
 	"github.com/goharbor/harbor/src/common/security/proxycachesecret"
 	"github.com/goharbor/harbor/src/controller/project"
@@ -28,6 +27,7 @@ import (
 	httpLib "github.com/goharbor/harbor/src/lib/http"
 	"github.com/goharbor/harbor/src/lib/log"
 	"github.com/goharbor/harbor/src/lib/orm"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"github.com/goharbor/harbor/src/pkg/reg/model"
 	"github.com/goharbor/harbor/src/server/middleware"
 	"io"
@@ -80,7 +80,7 @@ func handleBlob(w http.ResponseWriter, r *http.Request, next http.Handler) error
 	return nil
 }
 
-func preCheck(ctx context.Context) (art lib.ArtifactInfo, p *models.Project, ctl proxy.Controller, err error) {
+func preCheck(ctx context.Context) (art lib.ArtifactInfo, p *proModels.Project, ctl proxy.Controller, err error) {
 	none := lib.ArtifactInfo{}
 	art = lib.GetArtifactInfo(ctx)
 	if art == none {
@@ -152,7 +152,7 @@ func handleManifest(w http.ResponseWriter, r *http.Request, next http.Handler) e
 	return nil
 }
 
-func proxyManifestGet(ctx context.Context, w http.ResponseWriter, ctl proxy.Controller, p *models.Project, art lib.ArtifactInfo, remote proxy.RemoteInterface) error {
+func proxyManifestGet(ctx context.Context, w http.ResponseWriter, ctl proxy.Controller, p *proModels.Project, art lib.ArtifactInfo, remote proxy.RemoteInterface) error {
 	man, err := ctl.ProxyManifest(ctx, art, remote)
 	if err != nil {
 		return err
@@ -168,7 +168,7 @@ func proxyManifestGet(ctx context.Context, w http.ResponseWriter, ctl proxy.Cont
 	return nil
 }
 
-func canProxy(p *models.Project) bool {
+func canProxy(p *proModels.Project) bool {
 	if p.RegistryID < 1 {
 		return false
 	}
@@ -227,7 +227,7 @@ func DisableBlobAndManifestUploadMiddleware() func(http.Handler) http.Handler {
 	})
 }
 
-func proxyManifestHead(ctx context.Context, w http.ResponseWriter, ctl proxy.Controller, p *models.Project, art lib.ArtifactInfo, remote proxy.RemoteInterface) error {
+func proxyManifestHead(ctx context.Context, w http.ResponseWriter, ctl proxy.Controller, p *proModels.Project, art lib.ArtifactInfo, remote proxy.RemoteInterface) error {
 	exist, desc, err := ctl.HeadManifest(ctx, art, remote)
 	if err != nil {
 		return err
diff --git a/src/server/middleware/v2auth/auth_test.go b/src/server/middleware/v2auth/auth_test.go
index c32712eb1..ec6f07b24 100644
--- a/src/server/middleware/v2auth/auth_test.go
+++ b/src/server/middleware/v2auth/auth_test.go
@@ -19,6 +19,7 @@ import (
 	"fmt"
 	testutils "github.com/goharbor/harbor/src/common/utils/test"
 	"github.com/goharbor/harbor/src/lib/config"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 
 	"net/http"
 	"net/http/httptest"
@@ -28,7 +29,6 @@ import (
 	"testing"
 
 	"github.com/goharbor/harbor/src/common"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/rbac"
 	"github.com/goharbor/harbor/src/common/security"
 	"github.com/goharbor/harbor/src/controller/project"
@@ -46,20 +46,20 @@ func TestMain(m *testing.M) {
 	ctl := &projecttesting.Controller{}
 
 	mockGet := func(ctx context.Context,
-		projectIDOrName interface{}, options ...project.Option) (*models.Project, error) {
+		projectIDOrName interface{}, options ...project.Option) (*proModels.Project, error) {
 		name := projectIDOrName.(string)
 		id, _ := strconv.Atoi(strings.TrimPrefix(name, "project_"))
 		if id == 0 {
 			return nil, fmt.Errorf("%s not found", name)
 		}
-		return &models.Project{
+		return &proModels.Project{
 			ProjectID: int64(id),
 			Name:      name,
 		}, nil
 	}
 	mock.OnAnything(ctl, "Get").Return(
 		func(ctx context.Context,
-			projectIDOrName interface{}, options ...project.Option) *models.Project {
+			projectIDOrName interface{}, options ...project.Option) *proModels.Project {
 			p, _ := mockGet(ctx, projectIDOrName, options...)
 			return p
 		},
diff --git a/src/server/middleware/vulnerable/vulnerable_test.go b/src/server/middleware/vulnerable/vulnerable_test.go
index b39f12e5d..9f2fd45bf 100644
--- a/src/server/middleware/vulnerable/vulnerable_test.go
+++ b/src/server/middleware/vulnerable/vulnerable_test.go
@@ -16,12 +16,12 @@ package vulnerable
 
 import (
 	"fmt"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
 	"github.com/docker/distribution/manifest/manifestlist"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/security"
 	"github.com/goharbor/harbor/src/controller/artifact"
 	"github.com/goharbor/harbor/src/controller/artifact/processor/image"
@@ -54,7 +54,7 @@ type MiddlewareTestSuite struct {
 	scanChecker func() scan.Checker
 
 	artifact *artifact.Artifact
-	project  *models.Project
+	project  *proModels.Project
 
 	next http.Handler
 }
@@ -85,12 +85,12 @@ func (suite *MiddlewareTestSuite) SetupTest() {
 	suite.artifact.RepositoryName = "library/photon"
 	suite.artifact.Digest = "digest"
 
-	suite.project = &models.Project{
+	suite.project = &proModels.Project{
 		ProjectID: suite.artifact.ProjectID,
 		Name:      "library",
 		Metadata: map[string]string{
-			models.ProMetaPreventVul: "true",
-			models.ProMetaSeverity:   vuln.High.String(),
+			proModels.ProMetaPreventVul: "true",
+			proModels.ProMetaSeverity:   vuln.High.String(),
 		},
 	}
 
@@ -153,7 +153,7 @@ func (suite *MiddlewareTestSuite) TestGetProjectFailed() {
 
 func (suite *MiddlewareTestSuite) TestPreventionDisabled() {
 	mock.OnAnything(suite.artifactController, "GetByReference").Return(suite.artifact, nil)
-	suite.project.Metadata[models.ProMetaPreventVul] = "false"
+	suite.project.Metadata[proModels.ProMetaPreventVul] = "false"
 	mock.OnAnything(suite.projectController, "Get").Return(suite.project, nil)
 
 	req := suite.makeRequest()
diff --git a/src/server/v2.0/handler/project_metadata.go b/src/server/v2.0/handler/project_metadata.go
index be7a0ff45..db2efda48 100644
--- a/src/server/v2.0/handler/project_metadata.go
+++ b/src/server/v2.0/handler/project_metadata.go
@@ -17,11 +17,11 @@ package handler
 import (
 	"context"
 	"github.com/go-openapi/runtime/middleware"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/common/rbac"
 	"github.com/goharbor/harbor/src/controller/project"
 	"github.com/goharbor/harbor/src/controller/project/metadata"
 	"github.com/goharbor/harbor/src/lib/errors"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"github.com/goharbor/harbor/src/pkg/scan/vuln"
 	operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/project_metadata"
 	"strconv"
@@ -140,19 +140,19 @@ func (p *projectMetadataAPI) validate(metas map[string]string) (map[string]strin
 	}
 
 	switch key {
-	case models.ProMetaPublic, models.ProMetaEnableContentTrust,
-		models.ProMetaPreventVul, models.ProMetaAutoScan:
+	case proModels.ProMetaPublic, proModels.ProMetaEnableContentTrust,
+		proModels.ProMetaPreventVul, proModels.ProMetaAutoScan:
 		v, err := strconv.ParseBool(value)
 		if err != nil {
 			return nil, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("invalid value: %s", value)
 		}
 		metas[key] = strconv.FormatBool(v)
-	case models.ProMetaSeverity:
+	case proModels.ProMetaSeverity:
 		severity := vuln.ParseSeverityVersion3(strings.ToLower(value))
 		if severity == vuln.Unknown {
 			return nil, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("invalid value: %s", value)
 		}
-		metas[models.ProMetaSeverity] = strings.ToLower(severity.String())
+		metas[proModels.ProMetaSeverity] = strings.ToLower(severity.String())
 	default:
 		return nil, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("invalid key: %s", key)
 	}
diff --git a/src/testing/apitests/apilib/search.go b/src/testing/apitests/apilib/search.go
index e25ba0847..2bb4bb0e6 100644
--- a/src/testing/apitests/apilib/search.go
+++ b/src/testing/apitests/apilib/search.go
@@ -23,13 +23,13 @@
 package apilib
 
 import (
-	"github.com/goharbor/harbor/src/common/models"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 )
 
 type Search struct {
 
 	// Search results of the projects that matched the filter keywords.
-	Projects []models.Project `json:"project,omitempty"`
+	Projects []proModels.Project `json:"project,omitempty"`
 
 	// Search results of the repositories that matched the filter keywords.
 	Repositories []SearchRepository `json:"repository,omitempty"`
diff --git a/src/testing/controller/project/controller.go b/src/testing/controller/project/controller.go
index 65bfdb81d..f09270ba5 100644
--- a/src/testing/controller/project/controller.go
+++ b/src/testing/controller/project/controller.go
@@ -5,9 +5,12 @@ package project
 import (
 	context "context"
 
-	models "github.com/goharbor/harbor/src/common/models"
+	commonmodels "github.com/goharbor/harbor/src/common/models"
+
 	mock "github.com/stretchr/testify/mock"
 
+	models "github.com/goharbor/harbor/src/pkg/project/models"
+
 	project "github.com/goharbor/harbor/src/controller/project"
 
 	q "github.com/goharbor/harbor/src/lib/q"
@@ -186,11 +189,11 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, options ...proje
 }
 
 // ListRoles provides a mock function with given fields: ctx, projectID, u
-func (_m *Controller) ListRoles(ctx context.Context, projectID int64, u *models.User) ([]int, error) {
+func (_m *Controller) ListRoles(ctx context.Context, projectID int64, u *commonmodels.User) ([]int, error) {
 	ret := _m.Called(ctx, projectID, u)
 
 	var r0 []int
-	if rf, ok := ret.Get(0).(func(context.Context, int64, *models.User) []int); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, int64, *commonmodels.User) []int); ok {
 		r0 = rf(ctx, projectID, u)
 	} else {
 		if ret.Get(0) != nil {
@@ -199,7 +202,7 @@ func (_m *Controller) ListRoles(ctx context.Context, projectID int64, u *models.
 	}
 
 	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, int64, *models.User) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, int64, *commonmodels.User) error); ok {
 		r1 = rf(ctx, projectID, u)
 	} else {
 		r1 = ret.Error(1)
diff --git a/src/testing/pkg/project/manager.go b/src/testing/pkg/project/manager.go
index 16e1e68e9..d6b7bba90 100644
--- a/src/testing/pkg/project/manager.go
+++ b/src/testing/pkg/project/manager.go
@@ -5,7 +5,7 @@ package project
 import (
 	context "context"
 
-	models "github.com/goharbor/harbor/src/common/models"
+	models "github.com/goharbor/harbor/src/pkg/project/models"
 	mock "github.com/stretchr/testify/mock"
 
 	q "github.com/goharbor/harbor/src/lib/q"
diff --git a/src/testing/suite.go b/src/testing/suite.go
index 832179adb..cf0b0440f 100644
--- a/src/testing/suite.go
+++ b/src/testing/suite.go
@@ -16,7 +16,10 @@ package testing
 
 import (
 	"context"
+	"fmt"
+	"github.com/goharbor/harbor/src/common"
 	"github.com/goharbor/harbor/src/lib/config"
+	proModels "github.com/goharbor/harbor/src/pkg/project/models"
 	"io"
 	"math/rand"
 	"net/http"
@@ -26,7 +29,6 @@ import (
 
 	o "github.com/astaxie/beego/orm"
 	"github.com/goharbor/harbor/src/common/dao"
-	"github.com/goharbor/harbor/src/common/models"
 	"github.com/goharbor/harbor/src/lib/errors"
 	"github.com/goharbor/harbor/src/lib/orm"
 	"github.com/opencontainers/go-digest"
@@ -101,7 +103,9 @@ func (suite *Suite) WithProject(f func(int64, string), projectNames ...string) {
 		projectName = suite.RandString(5)
 	}
 
-	projectID, err := dao.AddProject(models.Project{
+	ctx := suite.Context()
+
+	projectID, err := suite.AddProject(ctx, &proModels.Project{
 		Name:    projectName,
 		OwnerID: 1,
 	})
@@ -110,7 +114,7 @@ func (suite *Suite) WithProject(f func(int64, string), projectNames ...string) {
 	}
 
 	defer func() {
-		dao.DeleteProject(projectID)
+		suite.DeleteProject(ctx, projectName, projectID)
 	}()
 
 	f(projectID, projectName)
@@ -158,3 +162,60 @@ func (suite *Suite) ExecSQL(query string, args ...interface{}) {
 func (suite *Suite) IsNotFoundErr(err error) bool {
 	return suite.True(errors.IsNotFoundErr(err))
 }
+
+// AddProject put here is to avoid import cycle
+func (suite *Suite) AddProject(ctx context.Context, project *proModels.Project) (int64, error) {
+	// Create create a project instance
+	var projectID int64
+
+	h := func(ctx context.Context) error {
+		o, err := orm.FromContext(ctx)
+		if err != nil {
+			return err
+		}
+
+		now := time.Now()
+		project.CreationTime = now
+		project.UpdateTime = now
+
+		projectID, err = o.Insert(project)
+		if err != nil {
+			return orm.WrapConflictError(err, "The project named %s already exists", project.Name)
+		}
+
+		member := &proModels.Member{
+			ProjectID:    projectID,
+			EntityID:     project.OwnerID,
+			Role:         common.RoleProjectAdmin,
+			EntityType:   common.UserMember,
+			CreationTime: now,
+			UpdateTime:   now,
+		}
+
+		if _, err := o.Insert(member); err != nil {
+			return err
+		}
+
+		return nil
+	}
+
+	if err := orm.WithTransaction(h)(ctx); err != nil {
+		return 0, err
+	}
+
+	return projectID, nil
+}
+
+// DeleteProject put here is to avoid import cycle
+func (suite *Suite) DeleteProject(ctx context.Context, projectName string, projectID int64) error {
+	o, err := orm.FromContext(ctx)
+	if err != nil {
+		return err
+	}
+	name := fmt.Sprintf("%s#%d", projectName, projectID)
+	sql := `update project
+		set deleted = true, name = ?
+		where project_id = ?`
+	_, err = o.Raw(sql, name, projectID).Exec()
+	return err
+}