mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-11-04 01:15:14 +00:00 
			
		
		
		
	Convert db backend to use Gorm, with basis for support
for Mysql and Postgres in addition to existing Sqlite
This commit is contained in:
		@@ -30,7 +30,7 @@ tasks:
 | 
				
			|||||||
              silent: true
 | 
					              silent: true
 | 
				
			||||||
            - cmd: rm -f dist/bin/*
 | 
					            - cmd: rm -f dist/bin/*
 | 
				
			||||||
              silent: true
 | 
					              silent: true
 | 
				
			||||||
            - cmd: go build -buildvcs=false -ldflags="-X main.commit={{.GIT_COMMIT}} -X main.version={{.VERSION}}" -o ../dist/bin/server ./cmd/server/main.go
 | 
					            - cmd: go build -tags 'json1' -buildvcs=false -ldflags="-X main.commit={{.GIT_COMMIT}} -X main.version={{.VERSION}}" -o ../dist/bin/server ./cmd/server/main.go
 | 
				
			||||||
              silent: true
 | 
					              silent: true
 | 
				
			||||||
            - cmd: go build -buildvcs=false -ldflags="-X main.commit={{.GIT_COMMIT}} -X main.version={{.VERSION}}" -o ../dist/bin/ipranges ./cmd/ipranges/main.go
 | 
					            - cmd: go build -buildvcs=false -ldflags="-X main.commit={{.GIT_COMMIT}} -X main.version={{.VERSION}}" -o ../dist/bin/ipranges ./cmd/ipranges/main.go
 | 
				
			||||||
              silent: true
 | 
					              silent: true
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,8 @@ import (
 | 
				
			|||||||
	"npm/internal/entity/certificate"
 | 
						"npm/internal/entity/certificate"
 | 
				
			||||||
	"npm/internal/entity/host"
 | 
						"npm/internal/entity/host"
 | 
				
			||||||
	"npm/internal/entity/setting"
 | 
						"npm/internal/entity/setting"
 | 
				
			||||||
 | 
						"npm/internal/entity/user"
 | 
				
			||||||
 | 
						"npm/internal/errors"
 | 
				
			||||||
	"npm/internal/jobqueue"
 | 
						"npm/internal/jobqueue"
 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -25,7 +27,7 @@ func main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	database.Migrate(func() {
 | 
						database.Migrate(func() {
 | 
				
			||||||
		setting.ApplySettings()
 | 
							setting.ApplySettings()
 | 
				
			||||||
		database.CheckSetup()
 | 
							checkSetup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Internal Job Queue
 | 
							// Internal Job Queue
 | 
				
			||||||
		jobqueue.Start()
 | 
							jobqueue.Start()
 | 
				
			||||||
@@ -41,7 +43,8 @@ func main() {
 | 
				
			|||||||
			if irq == syscall.SIGINT || irq == syscall.SIGTERM {
 | 
								if irq == syscall.SIGINT || irq == syscall.SIGTERM {
 | 
				
			||||||
				logger.Info("Got ", irq, " shutting server down ...")
 | 
									logger.Info("Got ", irq, " shutting server down ...")
 | 
				
			||||||
				// Close db
 | 
									// Close db
 | 
				
			||||||
				err := database.GetInstance().Close()
 | 
									sqlDB, _ := database.GetDB().DB()
 | 
				
			||||||
 | 
									err := sqlDB.Close()
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					logger.Error("DatabaseCloseError", err)
 | 
										logger.Error("DatabaseCloseError", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -52,3 +55,28 @@ func main() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// checkSetup Quick check by counting the number of users in the database
 | 
				
			||||||
 | 
					func checkSetup() {
 | 
				
			||||||
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						var count int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if db != nil {
 | 
				
			||||||
 | 
							db.Model(&user.Model{}).
 | 
				
			||||||
 | 
								Where("is_disabled = ?", false).
 | 
				
			||||||
 | 
								Where("is_system = ?", false).
 | 
				
			||||||
 | 
								Count(&count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if count == 0 {
 | 
				
			||||||
 | 
								logger.Warn("No users found, starting in Setup Mode")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								config.IsSetup = true
 | 
				
			||||||
 | 
								logger.Info("Application is setup")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if config.ErrorReporting {
 | 
				
			||||||
 | 
								logger.Warn("Error reporting is enabled - Application Errors WILL be sent to Sentry, you can disable this in the Settings interface")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							logger.Error("DatabaseError", errors.ErrDatabaseUnavailable)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								backend/db/schema.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								backend/db/schema.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					INSERT INTO "schema_migrations" (version) VALUES
 | 
				
			||||||
 | 
					  ('20201013035318'),
 | 
				
			||||||
 | 
					  ('20201013035839');
 | 
				
			||||||
@@ -14,7 +14,7 @@ var Assets embed.FS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// MigrationFiles are database migrations
 | 
					// MigrationFiles are database migrations
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//go:embed migrations/*.sql
 | 
					//go:embed migrations
 | 
				
			||||||
var MigrationFiles embed.FS
 | 
					var MigrationFiles embed.FS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NginxFiles hold nginx config templates
 | 
					// NginxFiles hold nginx config templates
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,40 +3,39 @@
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS `user`
 | 
					CREATE TABLE IF NOT EXISTS `user`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						name TEXT NOT NULL,
 | 
				
			||||||
	nickname TEXT NOT NULL,
 | 
						nickname TEXT NOT NULL,
 | 
				
			||||||
	email TEXT NOT NULL,
 | 
						email TEXT NOT NULL,
 | 
				
			||||||
	is_system INTEGER NOT NULL DEFAULT 0,
 | 
						is_system INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_disabled INTEGER NOT NULL DEFAULT 0,
 | 
						is_disabled INTEGER NOT NULL DEFAULT 0
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0
 | 
					 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `capability`
 | 
					CREATE TABLE IF NOT EXISTS `capability`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						name TEXT PRIMARY KEY,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
					 | 
				
			||||||
	UNIQUE (name)
 | 
						UNIQUE (name)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `user_has_capability`
 | 
					CREATE TABLE IF NOT EXISTS `user_has_capability`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	capability_id INTEGER NOT NULL,
 | 
						capability_name TEXT NOT NULL,
 | 
				
			||||||
	UNIQUE (user_id, capability_id),
 | 
						UNIQUE (user_id, capability_name),
 | 
				
			||||||
	FOREIGN KEY (capability_id) REFERENCES capability (id)
 | 
						FOREIGN KEY (capability_name) REFERENCES capability (name)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `auth`
 | 
					CREATE TABLE IF NOT EXISTS `auth`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	type TEXT NOT NULL,
 | 
						type TEXT NOT NULL,
 | 
				
			||||||
	secret TEXT NOT NULL,
 | 
						secret TEXT NOT NULL,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (user_id) REFERENCES user (id),
 | 
				
			||||||
	UNIQUE (user_id, type)
 | 
						UNIQUE (user_id, type)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -44,8 +43,9 @@ CREATE TABLE IF NOT EXISTS `auth`
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS `setting`
 | 
					CREATE TABLE IF NOT EXISTS `setting`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						name TEXT NOT NULL,
 | 
				
			||||||
	description TEXT NOT NULL DEFAULT "",
 | 
						description TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	value TEXT NOT NULL,
 | 
						value TEXT NOT NULL,
 | 
				
			||||||
@@ -55,8 +55,9 @@ CREATE TABLE IF NOT EXISTS `setting`
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS `audit_log`
 | 
					CREATE TABLE IF NOT EXISTS `audit_log`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	object_type TEXT NOT NULL,
 | 
						object_type TEXT NOT NULL,
 | 
				
			||||||
	object_id INTEGER NOT NULL,
 | 
						object_id INTEGER NOT NULL,
 | 
				
			||||||
@@ -68,36 +69,37 @@ CREATE TABLE IF NOT EXISTS `audit_log`
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS `certificate_authority`
 | 
					CREATE TABLE IF NOT EXISTS `certificate_authority`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						name TEXT NOT NULL,
 | 
				
			||||||
	acmesh_server TEXT NOT NULL DEFAULT "",
 | 
						acmesh_server TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	ca_bundle TEXT NOT NULL DEFAULT "",
 | 
						ca_bundle TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	is_wildcard_supported INTEGER NOT NULL DEFAULT 0, -- specific to each CA, acme v1 doesn't usually have wildcards
 | 
						is_wildcard_supported INTEGER NOT NULL DEFAULT 0, -- specific to each CA, acme v1 doesn't usually have wildcards
 | 
				
			||||||
	max_domains INTEGER NOT NULL DEFAULT 5, -- per request
 | 
						max_domains INTEGER NOT NULL DEFAULT 5, -- per request
 | 
				
			||||||
	is_readonly INTEGER NOT NULL DEFAULT 0,
 | 
						is_readonly INTEGER NOT NULL DEFAULT 0
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0
 | 
					 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `dns_provider`
 | 
					CREATE TABLE IF NOT EXISTS `dns_provider`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						name TEXT NOT NULL,
 | 
				
			||||||
	acmesh_name TEXT NOT NULL,
 | 
						acmesh_name TEXT NOT NULL,
 | 
				
			||||||
	dns_sleep INTEGER NOT NULL DEFAULT 0,
 | 
						dns_sleep INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	meta TEXT NOT NULL,
 | 
						meta TEXT NOT NULL,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (user_id) REFERENCES user (id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `certificate`
 | 
					CREATE TABLE IF NOT EXISTS `certificate`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	type TEXT NOT NULL, -- custom,dns,http
 | 
						type TEXT NOT NULL, -- custom,dns,http
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	certificate_authority_id INTEGER, -- 0 for a custom cert
 | 
						certificate_authority_id INTEGER, -- 0 for a custom cert
 | 
				
			||||||
@@ -109,7 +111,6 @@ CREATE TABLE IF NOT EXISTS `certificate`
 | 
				
			|||||||
	error_message text NOT NULL DEFAULT "",
 | 
						error_message text NOT NULL DEFAULT "",
 | 
				
			||||||
	meta TEXT NOT NULL,
 | 
						meta TEXT NOT NULL,
 | 
				
			||||||
	is_ecc INTEGER NOT NULL DEFAULT 0,
 | 
						is_ecc INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (user_id) REFERENCES user (id),
 | 
				
			||||||
	FOREIGN KEY (certificate_authority_id) REFERENCES certificate_authority (id),
 | 
						FOREIGN KEY (certificate_authority_id) REFERENCES certificate_authority (id),
 | 
				
			||||||
	FOREIGN KEY (dns_provider_id) REFERENCES dns_provider (id)
 | 
						FOREIGN KEY (dns_provider_id) REFERENCES dns_provider (id)
 | 
				
			||||||
@@ -118,8 +119,9 @@ CREATE TABLE IF NOT EXISTS `certificate`
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS `stream`
 | 
					CREATE TABLE IF NOT EXISTS `stream`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	listen_interface TEXT NOT NULL,
 | 
						listen_interface TEXT NOT NULL,
 | 
				
			||||||
	incoming_port INTEGER NOT NULL,
 | 
						incoming_port INTEGER NOT NULL,
 | 
				
			||||||
@@ -127,15 +129,15 @@ CREATE TABLE IF NOT EXISTS `stream`
 | 
				
			|||||||
	udp_forwarding INTEGER NOT NULL DEFAULT 0,
 | 
						udp_forwarding INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	advanced_config TEXT NOT NULL,
 | 
						advanced_config TEXT NOT NULL,
 | 
				
			||||||
	is_disabled INTEGER NOT NULL DEFAULT 0,
 | 
						is_disabled INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (user_id) REFERENCES user (id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `upstream`
 | 
					CREATE TABLE IF NOT EXISTS `upstream`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						name TEXT NOT NULL,
 | 
				
			||||||
	nginx_template_id INTEGER NOT NULL,
 | 
						nginx_template_id INTEGER NOT NULL,
 | 
				
			||||||
@@ -148,7 +150,6 @@ CREATE TABLE IF NOT EXISTS `upstream`
 | 
				
			|||||||
	advanced_config TEXT NOT NULL,
 | 
						advanced_config TEXT NOT NULL,
 | 
				
			||||||
	status TEXT NOT NULL DEFAULT "",
 | 
						status TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	error_message TEXT NOT NULL DEFAULT "",
 | 
						error_message TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (user_id) REFERENCES user (id),
 | 
				
			||||||
	FOREIGN KEY (nginx_template_id) REFERENCES nginx_template (id)
 | 
						FOREIGN KEY (nginx_template_id) REFERENCES nginx_template (id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@@ -156,49 +157,50 @@ CREATE TABLE IF NOT EXISTS `upstream`
 | 
				
			|||||||
CREATE TABLE IF NOT EXISTS `upstream_server`
 | 
					CREATE TABLE IF NOT EXISTS `upstream_server`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	upstream_id INTEGER NOT NULL,
 | 
						upstream_id INTEGER NOT NULL,
 | 
				
			||||||
	server TEXT NOT NULL,
 | 
						server TEXT NOT NULL,
 | 
				
			||||||
	weight INTEGER NOT NULL DEFAULT 0,
 | 
						weight INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	max_conns INTEGER NOT NULL DEFAULT 0,
 | 
						max_conns INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	max_fails INTEGER NOT NULL DEFAULT 0,
 | 
						max_fails INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	fail_timeout INTEGER NOT NULL DEFAULT 0,
 | 
						fail_timeout INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	backup INTEGER NOT NULL DEFAULT 0,
 | 
						is_backup INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (upstream_id) REFERENCES upstream (id)
 | 
						FOREIGN KEY (upstream_id) REFERENCES upstream (id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `access_list`
 | 
					CREATE TABLE IF NOT EXISTS `access_list`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						name TEXT NOT NULL,
 | 
				
			||||||
	meta TEXT NOT NULL,
 | 
						meta TEXT NOT NULL,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (user_id) REFERENCES user (id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `nginx_template`
 | 
					CREATE TABLE IF NOT EXISTS `nginx_template`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						name TEXT NOT NULL,
 | 
				
			||||||
	type TEXT NOT NULL,
 | 
						type TEXT NOT NULL,
 | 
				
			||||||
	template TEXT NOT NULL,
 | 
						template TEXT NOT NULL,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (user_id) REFERENCES user (id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `host`
 | 
					CREATE TABLE IF NOT EXISTS `host`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_on INTEGER NOT NULL DEFAULT 0,
 | 
						created_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	modified_on INTEGER NOT NULL DEFAULT 0,
 | 
						updated_at INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
 | 
						is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	user_id INTEGER NOT NULL,
 | 
						user_id INTEGER NOT NULL,
 | 
				
			||||||
	type TEXT NOT NULL,
 | 
						type TEXT NOT NULL,
 | 
				
			||||||
	nginx_template_id INTEGER NOT NULL,
 | 
						nginx_template_id INTEGER NOT NULL,
 | 
				
			||||||
@@ -222,7 +224,6 @@ CREATE TABLE IF NOT EXISTS `host`
 | 
				
			|||||||
	status TEXT NOT NULL DEFAULT "",
 | 
						status TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	error_message TEXT NOT NULL DEFAULT "",
 | 
						error_message TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	is_disabled INTEGER NOT NULL DEFAULT 0,
 | 
						is_disabled INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
					 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (user_id) REFERENCES user (id),
 | 
				
			||||||
	FOREIGN KEY (nginx_template_id) REFERENCES nginx_template (id),
 | 
						FOREIGN KEY (nginx_template_id) REFERENCES nginx_template (id),
 | 
				
			||||||
	FOREIGN KEY (upstream_id) REFERENCES upstream (id),
 | 
						FOREIGN KEY (upstream_id) REFERENCES upstream (id),
 | 
				
			||||||
@@ -25,14 +25,14 @@ INSERT INTO `capability` (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- Default error reporting setting
 | 
					-- Default error reporting setting
 | 
				
			||||||
INSERT INTO `setting` (
 | 
					INSERT INTO `setting` (
 | 
				
			||||||
	created_on,
 | 
						created_at,
 | 
				
			||||||
	modified_on,
 | 
						updated_at,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
	description,
 | 
						description,
 | 
				
			||||||
	value
 | 
						value
 | 
				
			||||||
) VALUES (
 | 
					) VALUES (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"error-reporting",
 | 
						"error-reporting",
 | 
				
			||||||
	"If enabled, any application errors are reported to Sentry. Sensitive information is not sent.",
 | 
						"If enabled, any application errors are reported to Sentry. Sensitive information is not sent.",
 | 
				
			||||||
	"true" -- remember this is json
 | 
						"true" -- remember this is json
 | 
				
			||||||
@@ -40,14 +40,14 @@ INSERT INTO `setting` (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- Default site
 | 
					-- Default site
 | 
				
			||||||
INSERT INTO `setting` (
 | 
					INSERT INTO `setting` (
 | 
				
			||||||
	created_on,
 | 
						created_at,
 | 
				
			||||||
	modified_on,
 | 
						updated_at,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
	description,
 | 
						description,
 | 
				
			||||||
	value
 | 
						value
 | 
				
			||||||
) VALUES (
 | 
					) VALUES (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"default-site",
 | 
						"default-site",
 | 
				
			||||||
	"What to show users who hit your Nginx server by default",
 | 
						"What to show users who hit your Nginx server by default",
 | 
				
			||||||
	'"welcome"' -- remember this is json
 | 
						'"welcome"' -- remember this is json
 | 
				
			||||||
@@ -56,56 +56,56 @@ INSERT INTO `setting` (
 | 
				
			|||||||
-- Default Certificate Authorities
 | 
					-- Default Certificate Authorities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
INSERT INTO `certificate_authority` (
 | 
					INSERT INTO `certificate_authority` (
 | 
				
			||||||
	created_on,
 | 
						created_at,
 | 
				
			||||||
	modified_on,
 | 
						updated_at,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
	acmesh_server,
 | 
						acmesh_server,
 | 
				
			||||||
	is_wildcard_supported,
 | 
						is_wildcard_supported,
 | 
				
			||||||
	max_domains,
 | 
						max_domains,
 | 
				
			||||||
	is_readonly
 | 
						is_readonly
 | 
				
			||||||
) VALUES (
 | 
					) VALUES (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"ZeroSSL",
 | 
						"ZeroSSL",
 | 
				
			||||||
	"zerossl",
 | 
						"zerossl",
 | 
				
			||||||
	1,
 | 
						1,
 | 
				
			||||||
	10,
 | 
						10,
 | 
				
			||||||
	1
 | 
						1
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"Let's Encrypt",
 | 
						"Let's Encrypt",
 | 
				
			||||||
	"https://acme-v02.api.letsencrypt.org/directory",
 | 
						"https://acme-v02.api.letsencrypt.org/directory",
 | 
				
			||||||
	1,
 | 
						1,
 | 
				
			||||||
	10,
 | 
						10,
 | 
				
			||||||
	1
 | 
						1
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"Buypass Go SSL",
 | 
						"Buypass Go SSL",
 | 
				
			||||||
	"https://api.buypass.com/acme/directory",
 | 
						"https://api.buypass.com/acme/directory",
 | 
				
			||||||
	0,
 | 
						0,
 | 
				
			||||||
	5,
 | 
						5,
 | 
				
			||||||
	1
 | 
						1
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"SSL.com",
 | 
						"SSL.com",
 | 
				
			||||||
	"ssl.com",
 | 
						"ssl.com",
 | 
				
			||||||
	0,
 | 
						0,
 | 
				
			||||||
	10,
 | 
						10,
 | 
				
			||||||
	1
 | 
						1
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"Let's Encrypt (Testing)",
 | 
						"Let's Encrypt (Testing)",
 | 
				
			||||||
	"https://acme-staging-v02.api.letsencrypt.org/directory",
 | 
						"https://acme-staging-v02.api.letsencrypt.org/directory",
 | 
				
			||||||
	1,
 | 
						1,
 | 
				
			||||||
	10,
 | 
						10,
 | 
				
			||||||
	1
 | 
						1
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"Buypass Go SSL (Testing)",
 | 
						"Buypass Go SSL (Testing)",
 | 
				
			||||||
	"https://api.test4.buypass.no/acme/directory",
 | 
						"https://api.test4.buypass.no/acme/directory",
 | 
				
			||||||
	0,
 | 
						0,
 | 
				
			||||||
@@ -115,15 +115,15 @@ INSERT INTO `certificate_authority` (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- System User
 | 
					-- System User
 | 
				
			||||||
INSERT INTO `user` (
 | 
					INSERT INTO `user` (
 | 
				
			||||||
	created_on,
 | 
						created_at,
 | 
				
			||||||
	modified_on,
 | 
						updated_at,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
	nickname,
 | 
						nickname,
 | 
				
			||||||
	email,
 | 
						email,
 | 
				
			||||||
	is_system
 | 
						is_system
 | 
				
			||||||
) VALUES (
 | 
					) VALUES (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	"System",
 | 
						"System",
 | 
				
			||||||
	"System",
 | 
						"System",
 | 
				
			||||||
	"system@localhost",
 | 
						"system@localhost",
 | 
				
			||||||
@@ -132,15 +132,15 @@ INSERT INTO `user` (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-- Host Templates
 | 
					-- Host Templates
 | 
				
			||||||
INSERT INTO `nginx_template` (
 | 
					INSERT INTO `nginx_template` (
 | 
				
			||||||
	created_on,
 | 
						created_at,
 | 
				
			||||||
	modified_on,
 | 
						updated_at,
 | 
				
			||||||
	user_id,
 | 
						user_id,
 | 
				
			||||||
	name,
 | 
						name,
 | 
				
			||||||
	type,
 | 
						type,
 | 
				
			||||||
	template
 | 
						template
 | 
				
			||||||
) VALUES (
 | 
					) VALUES (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
						(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
				
			||||||
	"Default Proxy Template",
 | 
						"Default Proxy Template",
 | 
				
			||||||
	"proxy",
 | 
						"proxy",
 | 
				
			||||||
@@ -262,29 +262,29 @@ server {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
"
 | 
					"
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
						(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
				
			||||||
	"Default Redirect Template",
 | 
						"Default Redirect Template",
 | 
				
			||||||
	"redirect",
 | 
						"redirect",
 | 
				
			||||||
	"# this is a redirect template"
 | 
						"# this is a redirect template"
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
						(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
				
			||||||
	"Default Dead Template",
 | 
						"Default Dead Template",
 | 
				
			||||||
	"dead",
 | 
						"dead",
 | 
				
			||||||
	"# this is a dead template"
 | 
						"# this is a dead template"
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
						(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
				
			||||||
	"Default Stream Template",
 | 
						"Default Stream Template",
 | 
				
			||||||
	"stream",
 | 
						"stream",
 | 
				
			||||||
	"# this is a stream template"
 | 
						"# this is a stream template"
 | 
				
			||||||
), (
 | 
					), (
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	strftime('%s', 'now'),
 | 
						unixepoch() * 1000,
 | 
				
			||||||
	(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
						(SELECT id FROM user WHERE is_system = 1 LIMIT 1),
 | 
				
			||||||
	"Default Upstream Template",
 | 
						"Default Upstream Template",
 | 
				
			||||||
	"upstream",
 | 
						"upstream",
 | 
				
			||||||
@@ -4,54 +4,61 @@ go 1.20
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/alexflint/go-arg v1.4.3
 | 
						github.com/alexflint/go-arg v1.4.3
 | 
				
			||||||
 | 
						github.com/amacneil/dbmate/v2 v2.3.0
 | 
				
			||||||
	github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible
 | 
						github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible
 | 
				
			||||||
	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
						github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
				
			||||||
	github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e
 | 
						github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e
 | 
				
			||||||
	github.com/fatih/color v1.13.0
 | 
						github.com/fatih/color v1.15.0
 | 
				
			||||||
	github.com/getsentry/sentry-go v0.17.0
 | 
						github.com/getsentry/sentry-go v0.21.0
 | 
				
			||||||
 | 
						github.com/glebarez/sqlite v1.8.0
 | 
				
			||||||
	github.com/go-chi/chi v4.1.2+incompatible
 | 
						github.com/go-chi/chi v4.1.2+incompatible
 | 
				
			||||||
	github.com/go-chi/cors v1.2.1
 | 
						github.com/go-chi/cors v1.2.1
 | 
				
			||||||
	github.com/go-chi/jwtauth v4.0.4+incompatible
 | 
						github.com/go-chi/jwtauth v4.0.4+incompatible
 | 
				
			||||||
	github.com/jc21/go-sse v0.0.0-20230307071053-2e6b1dbcb7ec
 | 
						github.com/jc21/go-sse v0.0.0-20230307071053-2e6b1dbcb7ec
 | 
				
			||||||
	github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760
 | 
						github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760
 | 
				
			||||||
	github.com/jmoiron/sqlx v1.3.5
 | 
					 | 
				
			||||||
	github.com/patrickmn/go-cache v2.1.0+incompatible
 | 
						github.com/patrickmn/go-cache v2.1.0+incompatible
 | 
				
			||||||
	github.com/qri-io/jsonschema v0.2.1
 | 
						github.com/qri-io/jsonschema v0.2.1
 | 
				
			||||||
	github.com/rotisserie/eris v0.5.4
 | 
						github.com/rotisserie/eris v0.5.4
 | 
				
			||||||
	github.com/stretchr/testify v1.7.0
 | 
						github.com/stretchr/testify v1.8.2
 | 
				
			||||||
	github.com/vrischmann/envconfig v1.3.0
 | 
						github.com/vrischmann/envconfig v1.3.0
 | 
				
			||||||
	golang.org/x/crypto v0.5.0
 | 
						golang.org/x/crypto v0.9.0
 | 
				
			||||||
	modernc.org/sqlite v1.21.1
 | 
						gorm.io/datatypes v1.2.0
 | 
				
			||||||
 | 
						gorm.io/driver/mysql v1.5.1
 | 
				
			||||||
 | 
						gorm.io/driver/postgres v1.5.2
 | 
				
			||||||
 | 
						gorm.io/gorm v1.25.1
 | 
				
			||||||
 | 
						gorm.io/plugin/soft_delete v1.2.1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/alexflint/go-scalar v1.2.0 // indirect
 | 
						github.com/alexflint/go-scalar v1.2.0 // indirect
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
	github.com/dustin/go-humanize v1.0.0 // indirect
 | 
						github.com/dustin/go-humanize v1.0.1 // indirect
 | 
				
			||||||
 | 
						github.com/glebarez/go-sqlite v1.21.1 // indirect
 | 
				
			||||||
 | 
						github.com/go-sql-driver/mysql v1.7.1 // indirect
 | 
				
			||||||
	github.com/google/uuid v1.3.0 // indirect
 | 
						github.com/google/uuid v1.3.0 // indirect
 | 
				
			||||||
	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 | 
						github.com/jackc/pgpassfile v1.0.0 // indirect
 | 
				
			||||||
 | 
						github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
 | 
				
			||||||
 | 
						github.com/jackc/pgx/v5 v5.3.1 // indirect
 | 
				
			||||||
 | 
						github.com/jinzhu/inflection v1.0.0 // indirect
 | 
				
			||||||
 | 
						github.com/jinzhu/now v1.1.5 // indirect
 | 
				
			||||||
	github.com/lestrrat-go/jspointer v0.0.0-20181205001929-82fadba7561c // indirect
 | 
						github.com/lestrrat-go/jspointer v0.0.0-20181205001929-82fadba7561c // indirect
 | 
				
			||||||
	github.com/lestrrat-go/option v1.0.1 // indirect
 | 
						github.com/lestrrat-go/option v1.0.1 // indirect
 | 
				
			||||||
	github.com/lestrrat-go/pdebug/v3 v3.0.1 // indirect
 | 
						github.com/lestrrat-go/pdebug/v3 v3.0.1 // indirect
 | 
				
			||||||
	github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb // indirect
 | 
						github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb // indirect
 | 
				
			||||||
 | 
						github.com/lib/pq v1.10.9 // indirect
 | 
				
			||||||
	github.com/mattn/go-colorable v0.1.13 // indirect
 | 
						github.com/mattn/go-colorable v0.1.13 // indirect
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.17 // indirect
 | 
						github.com/mattn/go-isatty v0.0.19 // indirect
 | 
				
			||||||
 | 
						github.com/mattn/go-sqlite3 v1.14.16 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	github.com/qri-io/jsonpointer v0.1.1 // indirect
 | 
						github.com/qri-io/jsonpointer v0.1.1 // indirect
 | 
				
			||||||
	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
				
			||||||
	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
 | 
						github.com/rogpeppe/go-internal v1.10.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.4.0 // indirect
 | 
						golang.org/x/sys v0.8.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.6.0 // indirect
 | 
						golang.org/x/text v0.9.0 // indirect
 | 
				
			||||||
	golang.org/x/tools v0.1.12 // indirect
 | 
					 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
	lukechampine.com/uint128 v1.2.0 // indirect
 | 
						modernc.org/libc v1.22.6 // indirect
 | 
				
			||||||
	modernc.org/cc/v3 v3.40.0 // indirect
 | 
					 | 
				
			||||||
	modernc.org/ccgo/v3 v3.16.13 // indirect
 | 
					 | 
				
			||||||
	modernc.org/libc v1.22.3 // indirect
 | 
					 | 
				
			||||||
	modernc.org/mathutil v1.5.0 // indirect
 | 
						modernc.org/mathutil v1.5.0 // indirect
 | 
				
			||||||
	modernc.org/memory v1.5.0 // indirect
 | 
						modernc.org/memory v1.5.0 // indirect
 | 
				
			||||||
	modernc.org/opt v0.1.3 // indirect
 | 
						modernc.org/sqlite v1.22.1 // indirect
 | 
				
			||||||
	modernc.org/strutil v1.1.3 // indirect
 | 
					 | 
				
			||||||
	modernc.org/token v1.0.1 // indirect
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										129
									
								
								backend/go.sum
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								backend/go.sum
									
									
									
									
									
								
							@@ -3,6 +3,8 @@ github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258m
 | 
				
			|||||||
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
 | 
					github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
 | 
				
			||||||
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
 | 
					github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
 | 
				
			||||||
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
 | 
					github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
 | 
				
			||||||
 | 
					github.com/amacneil/dbmate/v2 v2.3.0 h1:2Q90DiJmOxLQGgGELm+hCrIJejGldx9R3jN2++FsHQI=
 | 
				
			||||||
 | 
					github.com/amacneil/dbmate/v2 v2.3.0/go.mod h1:Vwi1HgvTnkuJ0GLBzT/GWG0ZnOm870FQXQL9qshSswM=
 | 
				
			||||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns=
 | 
					github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns=
 | 
				
			||||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
 | 
					github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
 | 
				
			||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
					github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
				
			||||||
@@ -13,12 +15,16 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
 | 
				
			|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
					github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
				
			||||||
github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e h1:2R8DvYLNr5DL25eWwpOdPno1eIbTNjJC0d7v8ti5cus=
 | 
					github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e h1:2R8DvYLNr5DL25eWwpOdPno1eIbTNjJC0d7v8ti5cus=
 | 
				
			||||||
github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e/go.mod h1:YjikoytuRI4q+GRd3xrOrKJN+Ayi2dwRomHLDDeMHfs=
 | 
					github.com/drexedam/gravatar v0.0.0-20210327211422-e94eea8c338e/go.mod h1:YjikoytuRI4q+GRd3xrOrKJN+Ayi2dwRomHLDDeMHfs=
 | 
				
			||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 | 
					github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 | 
				
			||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
					github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 | 
				
			||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
 | 
					github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
 | 
				
			||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
 | 
					github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
 | 
				
			||||||
github.com/getsentry/sentry-go v0.17.0 h1:UustVWnOoDFHBS7IJUB2QK/nB5pap748ZEp0swnQJak=
 | 
					github.com/getsentry/sentry-go v0.21.0 h1:c9l5F1nPF30JIppulk4veau90PK6Smu3abgVtVQWon4=
 | 
				
			||||||
github.com/getsentry/sentry-go v0.17.0/go.mod h1:B82dxtBvxG0KaPD8/hfSV+VcHD+Lg/xUS4JuQn1P4cM=
 | 
					github.com/getsentry/sentry-go v0.21.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
 | 
				
			||||||
 | 
					github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0y5NY=
 | 
				
			||||||
 | 
					github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
 | 
				
			||||||
 | 
					github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
 | 
				
			||||||
 | 
					github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
 | 
				
			||||||
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
 | 
					github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
 | 
				
			||||||
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
 | 
					github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
 | 
				
			||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
 | 
					github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
 | 
				
			||||||
@@ -26,20 +32,32 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz
 | 
				
			|||||||
github.com/go-chi/jwtauth v4.0.4+incompatible h1:LGIxg6YfvSBzxU2BljXbrzVc1fMlgqSKBQgKOGAVtPY=
 | 
					github.com/go-chi/jwtauth v4.0.4+incompatible h1:LGIxg6YfvSBzxU2BljXbrzVc1fMlgqSKBQgKOGAVtPY=
 | 
				
			||||||
github.com/go-chi/jwtauth v4.0.4+incompatible/go.mod h1:Q5EIArY/QnD6BdS+IyDw7B2m6iNbnPxtfd6/BcmtWbs=
 | 
					github.com/go-chi/jwtauth v4.0.4+incompatible/go.mod h1:Q5EIArY/QnD6BdS+IyDw7B2m6iNbnPxtfd6/BcmtWbs=
 | 
				
			||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 | 
					github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
 | 
					github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
					github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 | 
				
			||||||
 | 
					github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 | 
				
			||||||
 | 
					github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
 | 
				
			||||||
 | 
					github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
 | 
				
			||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
					github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
				
			||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
 | 
					github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
 | 
				
			||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
					github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
				
			||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
 | 
					github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
 | 
				
			||||||
 | 
					github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
 | 
				
			||||||
 | 
					github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
 | 
				
			||||||
 | 
					github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
 | 
				
			||||||
 | 
					github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
 | 
				
			||||||
 | 
					github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
 | 
				
			||||||
github.com/jc21/go-sse v0.0.0-20230307071053-2e6b1dbcb7ec h1:KKntwkZlM2w/88QiDyAeZ4th8grqtituzMW8qyapYzc=
 | 
					github.com/jc21/go-sse v0.0.0-20230307071053-2e6b1dbcb7ec h1:KKntwkZlM2w/88QiDyAeZ4th8grqtituzMW8qyapYzc=
 | 
				
			||||||
github.com/jc21/go-sse v0.0.0-20230307071053-2e6b1dbcb7ec/go.mod h1:4v5Xmm0eYuaWqKJ63XUV5YfQPoxtId3DgDytbnWhi+s=
 | 
					github.com/jc21/go-sse v0.0.0-20230307071053-2e6b1dbcb7ec/go.mod h1:4v5Xmm0eYuaWqKJ63XUV5YfQPoxtId3DgDytbnWhi+s=
 | 
				
			||||||
github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760 h1:7wxq2DIgtO36KLrFz1RldysO0WVvcYsD49G9tyAs01k=
 | 
					github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760 h1:7wxq2DIgtO36KLrFz1RldysO0WVvcYsD49G9tyAs01k=
 | 
				
			||||||
github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80=
 | 
					github.com/jc21/jsref v0.0.0-20210608024405-a97debfc4760/go.mod h1:yIq2t51OJgVsdRlPY68NAnyVdBH0kYXxDTFtUxOap80=
 | 
				
			||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
 | 
					github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
 | 
				
			||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
 | 
					github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
				
			||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 | 
					github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
				
			||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 | 
					github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
				
			||||||
 | 
					github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 | 
				
			||||||
 | 
					github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
				
			||||||
 | 
					github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 | 
				
			||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
					github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
				
			||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
					github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
				
			||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
					github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
				
			||||||
@@ -56,21 +74,20 @@ github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCi
 | 
				
			|||||||
github.com/lestrrat-go/structinfo v0.0.0-20190212233437-acd51874663b/go.mod h1:s2U6PowV3/Jobkx/S9d0XiPwOzs6niW3DIouw+7nZC8=
 | 
					github.com/lestrrat-go/structinfo v0.0.0-20190212233437-acd51874663b/go.mod h1:s2U6PowV3/Jobkx/S9d0XiPwOzs6niW3DIouw+7nZC8=
 | 
				
			||||||
github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb h1:DDg5u5lk2v8O8qxs8ecQkMUBj3tLW6wkSLzxxOyi1Ig=
 | 
					github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb h1:DDg5u5lk2v8O8qxs8ecQkMUBj3tLW6wkSLzxxOyi1Ig=
 | 
				
			||||||
github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb/go.mod h1:i+E8Uf04vf2QjOWyJdGY75vmG+4rxiZW2kIj1lTB5mo=
 | 
					github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb/go.mod h1:i+E8Uf04vf2QjOWyJdGY75vmG+4rxiZW2kIj1lTB5mo=
 | 
				
			||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 | 
					github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
 | 
				
			||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
					github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 | 
					 | 
				
			||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 | 
					github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
					github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
					github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
					github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
					 | 
				
			||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
					github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
 | 
					github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
					github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 | 
					github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
 | 
					github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
 | 
				
			||||||
 | 
					github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 | 
				
			||||||
 | 
					github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
 | 
				
			||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 | 
					github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 | 
				
			||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 | 
					 | 
				
			||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 | 
					github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 | 
				
			||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 | 
					github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 | 
				
			||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 | 
					github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 | 
				
			||||||
@@ -87,40 +104,43 @@ github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Ung
 | 
				
			|||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
				
			||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 | 
				
			||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
				
			||||||
 | 
					github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 | 
				
			||||||
 | 
					github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 | 
				
			||||||
github.com/rotisserie/eris v0.5.4 h1:Il6IvLdAapsMhvuOahHWiBnl1G++Q0/L5UIkI5mARSk=
 | 
					github.com/rotisserie/eris v0.5.4 h1:Il6IvLdAapsMhvuOahHWiBnl1G++Q0/L5UIkI5mARSk=
 | 
				
			||||||
github.com/rotisserie/eris v0.5.4/go.mod h1:Z/kgYTJiJtocxCbFfvRmO+QejApzG6zpyky9G1A4g9s=
 | 
					github.com/rotisserie/eris v0.5.4/go.mod h1:Z/kgYTJiJtocxCbFfvRmO+QejApzG6zpyky9G1A4g9s=
 | 
				
			||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 | 
					github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 | 
				
			||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
					github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
 | 
					github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
				
			||||||
 | 
					github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 | 
				
			||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
					github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 | 
					 | 
				
			||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 | 
				
			||||||
github.com/vrischmann/envconfig v1.3.0 h1:4XIvQTXznxmWMnjouj0ST5lFo/WAYf5Exgl3x82crEk=
 | 
					github.com/vrischmann/envconfig v1.3.0 h1:4XIvQTXznxmWMnjouj0ST5lFo/WAYf5Exgl3x82crEk=
 | 
				
			||||||
github.com/vrischmann/envconfig v1.3.0/go.mod h1:bbvxFYJdRSpXrhS63mBFtKJzkDiNkyArOLXtY6q0kuI=
 | 
					github.com/vrischmann/envconfig v1.3.0/go.mod h1:bbvxFYJdRSpXrhS63mBFtKJzkDiNkyArOLXtY6q0kuI=
 | 
				
			||||||
github.com/wacul/ptr v1.0.0/go.mod h1:BD0gjsZrCwtoR+yWDB9v2hQ8STlq9tT84qKfa+3txOc=
 | 
					github.com/wacul/ptr v1.0.0/go.mod h1:BD0gjsZrCwtoR+yWDB9v2hQ8STlq9tT84qKfa+3txOc=
 | 
				
			||||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
 | 
					github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w=
 | 
				
			||||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
 | 
					golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
 | 
				
			||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 | 
					golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
 | 
				
			||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
					golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
 | 
				
			||||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
 | 
				
			||||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
 | 
					golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
					golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
 | 
				
			||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
 | 
					golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 | 
				
			||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
					 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 | 
					 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 | 
					gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 | 
					gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 | 
				
			||||||
@@ -128,27 +148,26 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 | 
				
			|||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
 | 
					gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
 | 
				
			||||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
 | 
					gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
 | 
				
			||||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
 | 
					gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
 | 
				
			||||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
 | 
					gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
 | 
				
			||||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
 | 
					gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
 | 
				
			||||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
 | 
					gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
 | 
				
			||||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
 | 
					gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
 | 
				
			||||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
 | 
					gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
 | 
				
			||||||
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
 | 
					gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
 | 
				
			||||||
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
 | 
					gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
 | 
				
			||||||
 | 
					gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
 | 
				
			||||||
 | 
					gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
 | 
				
			||||||
 | 
					gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
 | 
				
			||||||
 | 
					gorm.io/plugin/soft_delete v1.2.1 h1:qx9D/c4Xu6w5KT8LviX8DgLcB9hkKl6JC9f44Tj7cGU=
 | 
				
			||||||
 | 
					gorm.io/plugin/soft_delete v1.2.1/go.mod h1:Zv7vQctOJTGOsJ/bWgrN1n3od0GBAZgnLjEx+cApLGk=
 | 
				
			||||||
 | 
					modernc.org/libc v1.22.6 h1:cbXU8R+A6aOjRuhsFh3nbDWXO/Hs4ClJRXYB11KmPDo=
 | 
				
			||||||
 | 
					modernc.org/libc v1.22.6/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
 | 
				
			||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
 | 
					modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
 | 
				
			||||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 | 
					modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 | 
				
			||||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
 | 
					modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
 | 
				
			||||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
 | 
					modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
 | 
				
			||||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
 | 
					modernc.org/sqlite v1.22.1 h1:P2+Dhp5FR1RlVRkQ3dDfCiv3Ok8XPxqpe70IjYVA9oE=
 | 
				
			||||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
 | 
					modernc.org/sqlite v1.22.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
 | 
				
			||||||
modernc.org/sqlite v1.21.1 h1:GyDFqNnESLOhwwDRaHGdp2jKLDzpyT/rNLglX3ZkMSU=
 | 
					 | 
				
			||||||
modernc.org/sqlite v1.21.1/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
 | 
					 | 
				
			||||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
 | 
					 | 
				
			||||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
 | 
					 | 
				
			||||||
modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws=
 | 
					 | 
				
			||||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
 | 
					 | 
				
			||||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
 | 
					 | 
				
			||||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ func GetAccessLists() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetAccessList() func(http.ResponseWriter, *http.Request) {
 | 
					func GetAccessList() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var accessListID int
 | 
							var accessListID uint
 | 
				
			||||||
		if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
 | 
							if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -81,7 +81,7 @@ func CreateAccessList() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func UpdateAccessList() func(http.ResponseWriter, *http.Request) {
 | 
					func UpdateAccessList() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var accessListID int
 | 
							var accessListID uint
 | 
				
			||||||
		if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
 | 
							if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -113,7 +113,7 @@ func UpdateAccessList() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func DeleteAccessList() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteAccessList() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var accessListID int
 | 
							var accessListID uint
 | 
				
			||||||
		if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
 | 
							if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,9 +14,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type setAuthModel struct {
 | 
					type setAuthModel struct {
 | 
				
			||||||
	Type          string `json:"type" db:"type"`
 | 
						Type          string
 | 
				
			||||||
	Secret        string `json:"secret,omitempty" db:"secret"`
 | 
						Secret        string
 | 
				
			||||||
	CurrentSecret string `json:"current_secret,omitempty"`
 | 
						CurrentSecret string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetAuth sets a auth method. This can be used for "me" and `2` for example
 | 
					// SetAuth sets a auth method. This can be used for "me" and `2` for example
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ func GetCertificateAuthorities() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
					func GetCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var caID int
 | 
							var caID uint
 | 
				
			||||||
		if caID, err = getURLParamInt(r, "caID"); err != nil {
 | 
							if caID, err = getURLParamInt(r, "caID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -92,7 +92,7 @@ func CreateCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func UpdateCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
					func UpdateCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var caID int
 | 
							var caID uint
 | 
				
			||||||
		if caID, err = getURLParamInt(r, "caID"); err != nil {
 | 
							if caID, err = getURLParamInt(r, "caID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -132,7 +132,7 @@ func UpdateCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func DeleteCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteCertificateAuthority() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var caID int
 | 
							var caID uint
 | 
				
			||||||
		if caID, err = getURLParamInt(r, "caID"); err != nil {
 | 
							if caID, err = getURLParamInt(r, "caID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,7 @@ func CreateCertificate() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
		var item certificate.Model
 | 
							var item certificate.Model
 | 
				
			||||||
		if fillObjectFromBody(w, r, "", &item) {
 | 
							if fillObjectFromBody(w, r, "", &item) {
 | 
				
			||||||
			// Get userID from token
 | 
								// Get userID from token
 | 
				
			||||||
			userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
 | 
								userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
 | 
				
			||||||
			item.UserID = userID
 | 
								item.UserID = userID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err := item.Save(); err != nil {
 | 
								if err := item.Save(); err != nil {
 | 
				
			||||||
@@ -131,7 +131,7 @@ func DownloadCertificate() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
// have a certificate id in the url. it will write errors to the output.
 | 
					// have a certificate id in the url. it will write errors to the output.
 | 
				
			||||||
func getCertificateFromRequest(w http.ResponseWriter, r *http.Request) *certificate.Model {
 | 
					func getCertificateFromRequest(w http.ResponseWriter, r *http.Request) *certificate.Model {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	var certificateID int
 | 
						var certificateID uint
 | 
				
			||||||
	if certificateID, err = getURLParamInt(r, "certificateID"); err != nil {
 | 
						if certificateID, err = getURLParamInt(r, "certificateID"); err != nil {
 | 
				
			||||||
		h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
							h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ func GetDNSProviders() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
					func GetDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var providerID int
 | 
							var providerID uint
 | 
				
			||||||
		if providerID, err = getURLParamInt(r, "providerID"); err != nil {
 | 
							if providerID, err = getURLParamInt(r, "providerID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -87,7 +87,7 @@ func CreateDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func UpdateDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
					func UpdateDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var providerID int
 | 
							var providerID uint
 | 
				
			||||||
		if providerID, err = getURLParamInt(r, "providerID"); err != nil {
 | 
							if providerID, err = getURLParamInt(r, "providerID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -122,7 +122,7 @@ func UpdateDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func DeleteDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteDNSProvider() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var providerID int
 | 
							var providerID uint
 | 
				
			||||||
		if providerID, err = getURLParamInt(r, "providerID"); err != nil {
 | 
							if providerID, err = getURLParamInt(r, "providerID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,6 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"npm/internal/api/context"
 | 
						"npm/internal/api/context"
 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
@@ -19,11 +18,6 @@ func getPageInfoFromRequest(r *http.Request) (model.PageInfo, error) {
 | 
				
			|||||||
	var pageInfo model.PageInfo
 | 
						var pageInfo model.PageInfo
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pageInfo.FromDate, pageInfo.ToDate, err = getDateRanges(r)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return pageInfo, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pageInfo.Offset, pageInfo.Limit, err = getPagination(r)
 | 
						pageInfo.Offset, pageInfo.Limit, err = getPagination(r)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return pageInfo, err
 | 
							return pageInfo, err
 | 
				
			||||||
@@ -34,32 +28,6 @@ func getPageInfoFromRequest(r *http.Request) (model.PageInfo, error) {
 | 
				
			|||||||
	return pageInfo, nil
 | 
						return pageInfo, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getDateRanges(r *http.Request) (time.Time, time.Time, error) {
 | 
					 | 
				
			||||||
	queryValues := r.URL.Query()
 | 
					 | 
				
			||||||
	from := queryValues.Get("from")
 | 
					 | 
				
			||||||
	fromDate := time.Now().AddDate(0, -1, 0) // 1 month ago by default
 | 
					 | 
				
			||||||
	to := queryValues.Get("to")
 | 
					 | 
				
			||||||
	toDate := time.Now()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if from != "" {
 | 
					 | 
				
			||||||
		var fromErr error
 | 
					 | 
				
			||||||
		fromDate, fromErr = time.Parse(time.RFC3339, from)
 | 
					 | 
				
			||||||
		if fromErr != nil {
 | 
					 | 
				
			||||||
			return fromDate, toDate, eris.Errorf("From date is not in correct format: %v", strings.ReplaceAll(time.RFC3339, "Z", "+"))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if to != "" {
 | 
					 | 
				
			||||||
		var toErr error
 | 
					 | 
				
			||||||
		toDate, toErr = time.Parse(time.RFC3339, to)
 | 
					 | 
				
			||||||
		if toErr != nil {
 | 
					 | 
				
			||||||
			return fromDate, toDate, eris.Errorf("To date is not in correct format: %v", strings.ReplaceAll(time.RFC3339, "Z", "+"))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return fromDate, toDate, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func getSortParameter(r *http.Request) []model.Sort {
 | 
					func getSortParameter(r *http.Request) []model.Sort {
 | 
				
			||||||
	var sortFields []model.Sort
 | 
						var sortFields []model.Sort
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -132,12 +100,11 @@ func getQueryVarBool(r *http.Request, varName string, required bool, defaultValu
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getURLParamInt(r *http.Request, varName string) (int, error) {
 | 
					func getURLParamInt(r *http.Request, varName string) (uint, error) {
 | 
				
			||||||
 | 
						var defaultValue uint = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	required := true
 | 
						required := true
 | 
				
			||||||
	defaultValue := 0
 | 
					 | 
				
			||||||
	paramStr := chi.URLParam(r, varName)
 | 
						paramStr := chi.URLParam(r, varName)
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	var paramInt int
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if paramStr == "" && required {
 | 
						if paramStr == "" && required {
 | 
				
			||||||
		return 0, eris.Errorf("%v was not supplied in the request", varName)
 | 
							return 0, eris.Errorf("%v was not supplied in the request", varName)
 | 
				
			||||||
@@ -145,11 +112,13 @@ func getURLParamInt(r *http.Request, varName string) (int, error) {
 | 
				
			|||||||
		return defaultValue, nil
 | 
							return defaultValue, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if paramInt, err = strconv.Atoi(paramStr); err != nil {
 | 
						// func ParseUint(s string, base int, bitSize int) (n uint64, err error)
 | 
				
			||||||
 | 
						paramUint, err := strconv.ParseUint(paramStr, 10, 32)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		return 0, eris.Wrapf(err, "%v is not a valid number", varName)
 | 
							return 0, eris.Wrapf(err, "%v is not a valid number", varName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return paramInt, nil
 | 
						return uint(paramUint), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getURLParamString(r *http.Request, varName string) (string, error) {
 | 
					func getURLParamString(r *http.Request, varName string) (string, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ func GetHosts() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetHost() func(http.ResponseWriter, *http.Request) {
 | 
					func GetHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var hostID int
 | 
							var hostID uint
 | 
				
			||||||
		if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
							if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -74,7 +74,7 @@ func CreateHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Get userID from token
 | 
							// Get userID from token
 | 
				
			||||||
		userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
 | 
							userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
 | 
				
			||||||
		newHost.UserID = userID
 | 
							newHost.UserID = userID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err = validator.ValidateHost(newHost); err != nil {
 | 
							if err = validator.ValidateHost(newHost); err != nil {
 | 
				
			||||||
@@ -103,7 +103,7 @@ func CreateHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func UpdateHost() func(http.ResponseWriter, *http.Request) {
 | 
					func UpdateHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var hostID int
 | 
							var hostID uint
 | 
				
			||||||
		if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
							if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -148,7 +148,7 @@ func UpdateHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func DeleteHost() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var hostID int
 | 
							var hostID uint
 | 
				
			||||||
		if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
							if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -173,7 +173,7 @@ func DeleteHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetHostNginxConfig(format string) func(http.ResponseWriter, *http.Request) {
 | 
					func GetHostNginxConfig(format string) func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var hostID int
 | 
							var hostID uint
 | 
				
			||||||
		if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
							if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ func GetNginxTemplates() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
					func GetNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var templateID int
 | 
							var templateID uint
 | 
				
			||||||
		if templateID, err = getURLParamInt(r, "templateID"); err != nil {
 | 
							if templateID, err = getURLParamInt(r, "templateID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -85,7 +85,7 @@ func CreateNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func UpdateNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
					func UpdateNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var templateID int
 | 
							var templateID uint
 | 
				
			||||||
		if templateID, err = getURLParamInt(r, "templateID"); err != nil {
 | 
							if templateID, err = getURLParamInt(r, "templateID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -122,7 +122,7 @@ func UpdateNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func DeleteNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteNginxTemplate() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var templateID int
 | 
							var templateID uint
 | 
				
			||||||
		if templateID, err = getURLParamInt(r, "templateID"); err != nil {
 | 
							if templateID, err = getURLParamInt(r, "templateID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ func GetStreams() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetStream() func(http.ResponseWriter, *http.Request) {
 | 
					func GetStream() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var hostID int
 | 
							var hostID uint
 | 
				
			||||||
		if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
							if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -85,7 +85,7 @@ func CreateStream() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func UpdateStream() func(http.ResponseWriter, *http.Request) {
 | 
					func UpdateStream() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var hostID int
 | 
							var hostID uint
 | 
				
			||||||
		if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
							if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -120,7 +120,7 @@ func UpdateStream() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func DeleteStream() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteStream() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var hostID int
 | 
							var hostID uint
 | 
				
			||||||
		if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
							if hostID, err = getURLParamInt(r, "hostID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,7 +93,7 @@ func RefreshToken() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
// Route: POST /tokens/sse
 | 
					// Route: POST /tokens/sse
 | 
				
			||||||
func NewSSEToken() func(http.ResponseWriter, *http.Request) {
 | 
					func NewSSEToken() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		userID := r.Context().Value(c.UserIDCtxKey).(int)
 | 
							userID := r.Context().Value(c.UserIDCtxKey).(uint)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Find user
 | 
							// Find user
 | 
				
			||||||
		userObj, userErr := user.GetByID(userID)
 | 
							userObj, userErr := user.GetByID(userID)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ func GetUpstreams() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetUpstream() func(http.ResponseWriter, *http.Request) {
 | 
					func GetUpstream() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var upstreamID int
 | 
							var upstreamID uint
 | 
				
			||||||
		if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
							if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -75,7 +75,7 @@ func CreateUpstream() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Get userID from token
 | 
							// Get userID from token
 | 
				
			||||||
		userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
 | 
							userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
 | 
				
			||||||
		newUpstream.UserID = userID
 | 
							newUpstream.UserID = userID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err = validator.ValidateUpstream(newUpstream); err != nil {
 | 
							if err = validator.ValidateUpstream(newUpstream); err != nil {
 | 
				
			||||||
@@ -99,7 +99,7 @@ func CreateUpstream() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func UpdateUpstream() func(http.ResponseWriter, *http.Request) {
 | 
					func UpdateUpstream() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var upstreamID int
 | 
							var upstreamID uint
 | 
				
			||||||
		if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
							if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -141,7 +141,7 @@ func UpdateUpstream() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func DeleteUpstream() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteUpstream() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var upstreamID int
 | 
							var upstreamID uint
 | 
				
			||||||
		if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
							if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -172,7 +172,7 @@ func DeleteUpstream() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
func GetUpstreamNginxConfig(format string) func(http.ResponseWriter, *http.Request) {
 | 
					func GetUpstreamNginxConfig(format string) func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		var upstreamID int
 | 
							var upstreamID uint
 | 
				
			||||||
		if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
							if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -121,14 +121,14 @@ func UpdateUser() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
// Route: DELETE /users/{userID}
 | 
					// Route: DELETE /users/{userID}
 | 
				
			||||||
func DeleteUser() func(http.ResponseWriter, *http.Request) {
 | 
					func DeleteUser() func(http.ResponseWriter, *http.Request) {
 | 
				
			||||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						return func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		var userID int
 | 
							var userID uint
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		if userID, err = getURLParamInt(r, "userID"); err != nil {
 | 
							if userID, err = getURLParamInt(r, "userID"); err != nil {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		myUserID, _ := r.Context().Value(c.UserIDCtxKey).(int)
 | 
							myUserID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
 | 
				
			||||||
		if myUserID == userID {
 | 
							if myUserID == userID {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot delete yourself!", nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot delete yourself!", nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -224,11 +224,11 @@ func DeleteUsers() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getUserIDFromRequest(r *http.Request) (int, bool, error) {
 | 
					func getUserIDFromRequest(r *http.Request) (uint, bool, error) {
 | 
				
			||||||
	userIDstr := chi.URLParam(r, "userID")
 | 
						userIDstr := chi.URLParam(r, "userID")
 | 
				
			||||||
	selfUserID, _ := r.Context().Value(c.UserIDCtxKey).(int)
 | 
						selfUserID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var userID int
 | 
						var userID uint
 | 
				
			||||||
	self := false
 | 
						self := false
 | 
				
			||||||
	if userIDstr == "me" {
 | 
						if userIDstr == "me" {
 | 
				
			||||||
		// Get user id from Token
 | 
							// Get user id from Token
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ func Enforce(permission string) func(http.Handler) http.Handler {
 | 
				
			|||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				userID := int(claims["uid"].(float64))
 | 
									userID := uint(claims["uid"].(float64))
 | 
				
			||||||
				_, enabled := user.IsEnabled(userID)
 | 
									_, enabled := user.IsEnabled(userID)
 | 
				
			||||||
				if token == nil || !token.Valid || !enabled {
 | 
									if token == nil || !token.Valid || !enabled {
 | 
				
			||||||
					h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil)
 | 
										h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ func SSEAuth(next http.Handler) http.Handler {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		userID := int(claims["uid"].(float64))
 | 
							userID := uint(claims["uid"].(float64))
 | 
				
			||||||
		_, enabled := user.IsEnabled(userID)
 | 
							_, enabled := user.IsEnabled(userID)
 | 
				
			||||||
		if token == nil || !token.Valid || !enabled || !claims.VerifyIssuer("sse", true) {
 | 
							if token == nil || !token.Valid || !enabled || !claims.VerifyIssuer("sse", true) {
 | 
				
			||||||
			h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil)
 | 
								h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,15 +8,6 @@ import (
 | 
				
			|||||||
	"npm/internal/api/middleware"
 | 
						"npm/internal/api/middleware"
 | 
				
			||||||
	"npm/internal/api/schema"
 | 
						"npm/internal/api/schema"
 | 
				
			||||||
	"npm/internal/config"
 | 
						"npm/internal/config"
 | 
				
			||||||
	"npm/internal/entity/accesslist"
 | 
					 | 
				
			||||||
	"npm/internal/entity/certificate"
 | 
					 | 
				
			||||||
	"npm/internal/entity/certificateauthority"
 | 
					 | 
				
			||||||
	"npm/internal/entity/dnsprovider"
 | 
					 | 
				
			||||||
	"npm/internal/entity/host"
 | 
					 | 
				
			||||||
	"npm/internal/entity/nginxtemplate"
 | 
					 | 
				
			||||||
	"npm/internal/entity/setting"
 | 
					 | 
				
			||||||
	"npm/internal/entity/stream"
 | 
					 | 
				
			||||||
	"npm/internal/entity/upstream"
 | 
					 | 
				
			||||||
	"npm/internal/entity/user"
 | 
						"npm/internal/entity/user"
 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
	"npm/internal/serverevents"
 | 
						"npm/internal/serverevents"
 | 
				
			||||||
@@ -102,7 +93,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				r.With(middleware.Enforce(user.CapabilityUsersManage)).Route("/", func(r chi.Router) {
 | 
									r.With(middleware.Enforce(user.CapabilityUsersManage)).Route("/", func(r chi.Router) {
 | 
				
			||||||
					// List
 | 
										// List
 | 
				
			||||||
					r.With(middleware.Enforce(user.CapabilityUsersManage), middleware.Filters(user.GetFilterSchema())).
 | 
										// r.With(middleware.Enforce(user.CapabilityUsersManage), middleware.Filters(user.GetFilterSchema())).
 | 
				
			||||||
 | 
										r.With(middleware.Enforce(user.CapabilityUsersManage)).
 | 
				
			||||||
						Get("/", handler.GetUsers())
 | 
											Get("/", handler.GetUsers())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// Specific Item
 | 
										// Specific Item
 | 
				
			||||||
@@ -132,8 +124,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Settings
 | 
							// Settings
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true), middleware.Enforce(user.CapabilitySettingsManage)).Route("/settings", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true), middleware.Enforce(user.CapabilitySettingsManage)).Route("/settings", func(r chi.Router) {
 | 
				
			||||||
			r.With(middleware.Filters(setting.GetFilterSchema())).
 | 
								// r.With(middleware.Filters(setting.GetFilterSchema())).
 | 
				
			||||||
				Get("/", handler.GetSettings())
 | 
								r.Get("/", handler.GetSettings())
 | 
				
			||||||
			r.Get("/{name}", handler.GetSetting())
 | 
								r.Get("/{name}", handler.GetSetting())
 | 
				
			||||||
			r.With(middleware.EnforceRequestSchema(schema.CreateSetting())).
 | 
								r.With(middleware.EnforceRequestSchema(schema.CreateSetting())).
 | 
				
			||||||
				Post("/", handler.CreateSetting())
 | 
									Post("/", handler.CreateSetting())
 | 
				
			||||||
@@ -144,7 +136,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// Access Lists
 | 
							// Access Lists
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/access-lists", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/access-lists", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Filters(accesslist.GetFilterSchema()), middleware.Enforce(user.CapabilityAccessListsView)).
 | 
								// r.With(middleware.Filters(accesslist.GetFilterSchema()), middleware.Enforce(user.CapabilityAccessListsView)).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityAccessListsView)).
 | 
				
			||||||
				Get("/", handler.GetAccessLists())
 | 
									Get("/", handler.GetAccessLists())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
@@ -166,7 +159,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// DNS Providers
 | 
							// DNS Providers
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/dns-providers", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/dns-providers", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Enforce(user.CapabilityDNSProvidersView), middleware.Filters(dnsprovider.GetFilterSchema())).
 | 
								// r.With(middleware.Enforce(user.CapabilityDNSProvidersView), middleware.Filters(dnsprovider.GetFilterSchema())).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityDNSProvidersView)).
 | 
				
			||||||
				Get("/", handler.GetDNSProviders())
 | 
									Get("/", handler.GetDNSProviders())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
@@ -194,7 +188,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// Certificate Authorities
 | 
							// Certificate Authorities
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/certificate-authorities", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/certificate-authorities", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Enforce(user.CapabilityCertificateAuthoritiesView), middleware.Filters(certificateauthority.GetFilterSchema())).
 | 
								// r.With(middleware.Enforce(user.CapabilityCertificateAuthoritiesView), middleware.Filters(certificateauthority.GetFilterSchema())).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityCertificateAuthoritiesView)).
 | 
				
			||||||
				Get("/", handler.GetCertificateAuthorities())
 | 
									Get("/", handler.GetCertificateAuthorities())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
@@ -216,7 +211,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// Certificates
 | 
							// Certificates
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/certificates", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/certificates", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Enforce(user.CapabilityCertificatesView), middleware.Filters(certificate.GetFilterSchema())).
 | 
								// r.With(middleware.Enforce(user.CapabilityCertificatesView), middleware.Filters(certificate.GetFilterSchema())).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityCertificatesView)).
 | 
				
			||||||
				Get("/", handler.GetCertificates())
 | 
									Get("/", handler.GetCertificates())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
@@ -241,7 +237,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// Hosts
 | 
							// Hosts
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/hosts", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/hosts", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Enforce(user.CapabilityHostsView), middleware.Filters(host.GetFilterSchema())).
 | 
								// r.With(middleware.Enforce(user.CapabilityHostsView), middleware.Filters(host.GetFilterSchema())).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityHostsView)).
 | 
				
			||||||
				Get("/", handler.GetHosts())
 | 
									Get("/", handler.GetHosts())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
@@ -265,7 +262,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// Nginx Templates
 | 
							// Nginx Templates
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/nginx-templates", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/nginx-templates", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Enforce(user.CapabilityNginxTemplatesView), middleware.Filters(nginxtemplate.GetFilterSchema())).
 | 
								// r.With(middleware.Enforce(user.CapabilityNginxTemplatesView), middleware.Filters(nginxtemplate.GetFilterSchema())).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityNginxTemplatesView)).
 | 
				
			||||||
				Get("/", handler.GetNginxTemplates())
 | 
									Get("/", handler.GetNginxTemplates())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
@@ -287,7 +285,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// Streams
 | 
							// Streams
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/streams", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/streams", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Enforce(user.CapabilityStreamsView), middleware.Filters(stream.GetFilterSchema())).
 | 
								// r.With(middleware.Enforce(user.CapabilityStreamsView), middleware.Filters(stream.GetFilterSchema())).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityStreamsView)).
 | 
				
			||||||
				Get("/", handler.GetStreams())
 | 
									Get("/", handler.GetStreams())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
@@ -309,7 +308,8 @@ func applyRoutes(r chi.Router) chi.Router {
 | 
				
			|||||||
		// Upstreams
 | 
							// Upstreams
 | 
				
			||||||
		r.With(middleware.EnforceSetup(true)).Route("/upstreams", func(r chi.Router) {
 | 
							r.With(middleware.EnforceSetup(true)).Route("/upstreams", func(r chi.Router) {
 | 
				
			||||||
			// List
 | 
								// List
 | 
				
			||||||
			r.With(middleware.Enforce(user.CapabilityHostsView), middleware.Filters(upstream.GetFilterSchema())).
 | 
								// r.With(middleware.Enforce(user.CapabilityHostsView), middleware.Filters(upstream.GetFilterSchema())).
 | 
				
			||||||
 | 
								r.With(middleware.Enforce(user.CapabilityHostsView)).
 | 
				
			||||||
				Get("/", handler.GetUpstreams())
 | 
									Get("/", handler.GetUpstreams())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create
 | 
								// Create
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								backend/internal/config/acmesh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								backend/internal/config/acmesh.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type acmesh struct {
 | 
				
			||||||
 | 
						Home       string `json:"home" envconfig:"optional,default=/data/.acme.sh"`
 | 
				
			||||||
 | 
						ConfigHome string `json:"config_home" envconfig:"optional,default=/data/.acme.sh/config"`
 | 
				
			||||||
 | 
						CertHome   string `json:"cert_home" envconfig:"optional,default=/data/.acme.sh/certs"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetWellknown returns the well known path
 | 
				
			||||||
 | 
					func (a *acmesh) GetWellknown() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%s/.well-known", a.Home)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										79
									
								
								backend/internal/config/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								backend/internal/config/db.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						DatabaseSqlite   = "sqlite"
 | 
				
			||||||
 | 
						DatabasePostgres = "postgres"
 | 
				
			||||||
 | 
						DatabaseMysql    = "mysql"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type db struct {
 | 
				
			||||||
 | 
						Driver   string `json:"driver" envconfig:"optional,default=sqlite"`
 | 
				
			||||||
 | 
						Host     string `json:"host" envconfig:"optional,default="`
 | 
				
			||||||
 | 
						Port     int    `json:"port" envconfig:"optional,default="`
 | 
				
			||||||
 | 
						Username string `json:"username" envconfig:"optional,default="`
 | 
				
			||||||
 | 
						Password string `json:"password" envconfig:"optional,default="`
 | 
				
			||||||
 | 
						Name     string `json:"name" envconfig:"optional,default="`
 | 
				
			||||||
 | 
						SSLMode  string `json:"sslmode" envconfig:"optional,default=deisable"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetDriver returns the lowercase driver name
 | 
				
			||||||
 | 
					func (d *db) GetDriver() string {
 | 
				
			||||||
 | 
						return strings.ToLower(d.Driver)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetGormConnectURL is used by Gorm
 | 
				
			||||||
 | 
					func (d *db) GetGormConnectURL() string {
 | 
				
			||||||
 | 
						switch d.GetDriver() {
 | 
				
			||||||
 | 
						case DatabaseSqlite:
 | 
				
			||||||
 | 
							return fmt.Sprintf("%s/nginxproxymanager.db", Configuration.DataFolder)
 | 
				
			||||||
 | 
						case DatabasePostgres:
 | 
				
			||||||
 | 
							return fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=UTC",
 | 
				
			||||||
 | 
								d.Host,
 | 
				
			||||||
 | 
								d.Username,
 | 
				
			||||||
 | 
								d.Password,
 | 
				
			||||||
 | 
								d.Name,
 | 
				
			||||||
 | 
								d.Port,
 | 
				
			||||||
 | 
								d.SSLMode,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						case DatabaseMysql:
 | 
				
			||||||
 | 
							return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
 | 
				
			||||||
 | 
								d.Username,
 | 
				
			||||||
 | 
								d.Password,
 | 
				
			||||||
 | 
								d.Host,
 | 
				
			||||||
 | 
								d.Port,
 | 
				
			||||||
 | 
								d.Name,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetDBMateConnectURL is used by Dbmate
 | 
				
			||||||
 | 
					func (d *db) GetDBMateConnectURL() string {
 | 
				
			||||||
 | 
						switch d.GetDriver() {
 | 
				
			||||||
 | 
						case DatabaseSqlite:
 | 
				
			||||||
 | 
							return fmt.Sprintf("sqlite:%s/nginxproxymanager.db", Configuration.DataFolder)
 | 
				
			||||||
 | 
						case DatabasePostgres:
 | 
				
			||||||
 | 
							return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
 | 
				
			||||||
 | 
								d.Username,
 | 
				
			||||||
 | 
								d.Password,
 | 
				
			||||||
 | 
								d.Host,
 | 
				
			||||||
 | 
								d.Port,
 | 
				
			||||||
 | 
								d.Name,
 | 
				
			||||||
 | 
								d.SSLMode,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						case DatabaseMysql:
 | 
				
			||||||
 | 
							return fmt.Sprintf("mysql://%s:%s@%s:%d/%s",
 | 
				
			||||||
 | 
								d.Username,
 | 
				
			||||||
 | 
								d.Password,
 | 
				
			||||||
 | 
								d.Host,
 | 
				
			||||||
 | 
								d.Port,
 | 
				
			||||||
 | 
								d.Name,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
package config
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,22 +29,12 @@ type log struct {
 | 
				
			|||||||
	Format string `json:"format" envconfig:"optional,default=nice"`
 | 
						Format string `json:"format" envconfig:"optional,default=nice"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type acmesh struct {
 | 
					 | 
				
			||||||
	Home       string `json:"home" envconfig:"optional,default=/data/.acme.sh"`
 | 
					 | 
				
			||||||
	ConfigHome string `json:"config_home" envconfig:"optional,default=/data/.acme.sh/config"`
 | 
					 | 
				
			||||||
	CertHome   string `json:"cert_home" envconfig:"optional,default=/data/.acme.sh/certs"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Configuration is the main configuration object
 | 
					// Configuration is the main configuration object
 | 
				
			||||||
var Configuration struct {
 | 
					var Configuration struct {
 | 
				
			||||||
	DataFolder  string `json:"data_folder" envconfig:"optional,default=/data"`
 | 
						DataFolder  string `json:"data_folder" envconfig:"optional,default=/data"`
 | 
				
			||||||
	DisableIPV4 bool   `json:"disable_ipv4" envconfig:"optional"`
 | 
						DisableIPV4 bool   `json:"disable_ipv4" envconfig:"optional"`
 | 
				
			||||||
	DisableIPV6 bool   `json:"disable_ipv6" envconfig:"optional"`
 | 
						DisableIPV6 bool   `json:"disable_ipv6" envconfig:"optional"`
 | 
				
			||||||
	Acmesh      acmesh `json:"acmesh"`
 | 
						Acmesh      acmesh `json:"acmesh"`
 | 
				
			||||||
 | 
						DB          db     `json:"db"`
 | 
				
			||||||
	Log         log    `json:"log"`
 | 
						Log         log    `json:"log"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetWellknown returns the well known path
 | 
					 | 
				
			||||||
func (a *acmesh) GetWellknown() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("%s/.well-known", a.Home)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										80
									
								
								backend/internal/database/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								backend/internal/database/db.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					package database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"npm/internal/config"
 | 
				
			||||||
 | 
						"npm/internal/logger"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/glebarez/sqlite"
 | 
				
			||||||
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
 | 
						"gorm.io/driver/mysql"
 | 
				
			||||||
 | 
						"gorm.io/driver/postgres"
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
						"gorm.io/gorm/schema"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var dbInstance *gorm.DB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewDB creates a new connection
 | 
				
			||||||
 | 
					func NewDB() {
 | 
				
			||||||
 | 
						logger.Info("Creating new DB instance using %s", strings.ToLower(config.Configuration.DB.Driver))
 | 
				
			||||||
 | 
						db, err := connect()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Error("DatabaseConnectError", err)
 | 
				
			||||||
 | 
						} else if db != nil {
 | 
				
			||||||
 | 
							dbInstance = db
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetDB returns an existing or new instance
 | 
				
			||||||
 | 
					func GetDB() *gorm.DB {
 | 
				
			||||||
 | 
						if dbInstance == nil {
 | 
				
			||||||
 | 
							NewDB()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dbInstance
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func connect() (*gorm.DB, error) {
 | 
				
			||||||
 | 
						var d gorm.Dialector
 | 
				
			||||||
 | 
						dsn := config.Configuration.DB.GetGormConnectURL()
 | 
				
			||||||
 | 
						switch strings.ToLower(config.Configuration.DB.Driver) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case config.DatabaseSqlite:
 | 
				
			||||||
 | 
							// autocreate(dsn)
 | 
				
			||||||
 | 
							d = sqlite.Open(dsn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case config.DatabasePostgres:
 | 
				
			||||||
 | 
							d = postgres.Open(dsn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case config.DatabaseMysql:
 | 
				
			||||||
 | 
							d = mysql.Open(dsn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil, eris.New(fmt.Sprintf("Database driver %s is not supported. Valid options are: %s, %s or %s", config.Configuration.DB.Driver, config.DatabaseSqlite, config.DatabasePostgres, config.DatabaseMysql))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return gorm.Open(d, &gorm.Config{
 | 
				
			||||||
 | 
							// see: https://gorm.io/docs/gorm_config.html
 | 
				
			||||||
 | 
							NamingStrategy: schema.NamingStrategy{
 | 
				
			||||||
 | 
								SingularTable: true,
 | 
				
			||||||
 | 
								NoLowerCase:   true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							PrepareStmt: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					func autocreate(dbFile string) {
 | 
				
			||||||
 | 
						if _, err := os.Stat(dbFile); os.IsNotExist(err) {
 | 
				
			||||||
 | 
							// Create it
 | 
				
			||||||
 | 
							logger.Info("Creating Sqlite DB: %s", dbFile)
 | 
				
			||||||
 | 
							// nolint: gosec
 | 
				
			||||||
 | 
							_, err = os.Create(dbFile)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								logger.Error("FileCreateError", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
@@ -1,46 +1,8 @@
 | 
				
			|||||||
package database
 | 
					package database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
	"npm/internal/util"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	// DateFormat for DateFormat
 | 
						// DateFormat for DateFormat
 | 
				
			||||||
	DateFormat = "2006-01-02"
 | 
						DateFormat = "2006-01-02"
 | 
				
			||||||
	// DateTimeFormat for DateTimeFormat
 | 
						// DateTimeFormat for DateTimeFormat
 | 
				
			||||||
	DateTimeFormat = "2006-01-02T15:04:05"
 | 
						DateTimeFormat = "2006-01-02T15:04:05"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetByQuery returns a row given a query, populating the model given
 | 
					 | 
				
			||||||
func GetByQuery(model interface{}, query string, params []interface{}) error {
 | 
					 | 
				
			||||||
	db := GetInstance()
 | 
					 | 
				
			||||||
	if db != nil {
 | 
					 | 
				
			||||||
		err := db.Get(model, query, params...)
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// BuildOrderBySQL takes a `Sort` slice and constructs a query fragment
 | 
					 | 
				
			||||||
func BuildOrderBySQL(columns []string, sort *[]model.Sort) (string, []model.Sort) {
 | 
					 | 
				
			||||||
	var sortStrings []string
 | 
					 | 
				
			||||||
	var newSort []model.Sort
 | 
					 | 
				
			||||||
	for _, sortItem := range *sort {
 | 
					 | 
				
			||||||
		if util.SliceContainsItem(columns, sortItem.Field) {
 | 
					 | 
				
			||||||
			sortStrings = append(sortStrings, fmt.Sprintf("`%s` %s", sortItem.Field, sortItem.Direction))
 | 
					 | 
				
			||||||
			newSort = append(newSort, sortItem)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(sortStrings) > 0 {
 | 
					 | 
				
			||||||
		return fmt.Sprintf("ORDER BY %s", strings.Join(sortStrings, ", ")), newSort
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return "", newSort
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,203 +1,44 @@
 | 
				
			|||||||
package database
 | 
					package database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/fs"
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"npm/embed"
 | 
						"npm/embed"
 | 
				
			||||||
 | 
						"npm/internal/config"
 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
	"npm/internal/util"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/jmoiron/sqlx"
 | 
						"github.com/amacneil/dbmate/v2/pkg/dbmate"
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						_ "github.com/amacneil/dbmate/v2/pkg/driver/postgres"
 | 
				
			||||||
 | 
						_ "github.com/amacneil/dbmate/v2/pkg/driver/sqlite"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MigrationConfiguration options for the migrator.
 | 
					 | 
				
			||||||
type MigrationConfiguration struct {
 | 
					 | 
				
			||||||
	Table string `json:"table"`
 | 
					 | 
				
			||||||
	mux   sync.Mutex
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Default migrator configuration
 | 
					 | 
				
			||||||
var mConfiguration = MigrationConfiguration{
 | 
					 | 
				
			||||||
	Table: "migration",
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ConfigureMigrator and will return error if missing required fields.
 | 
					 | 
				
			||||||
func ConfigureMigrator(c *MigrationConfiguration) error {
 | 
					 | 
				
			||||||
	// ensure updates to the config are atomic
 | 
					 | 
				
			||||||
	mConfiguration.mux.Lock()
 | 
					 | 
				
			||||||
	defer mConfiguration.mux.Unlock()
 | 
					 | 
				
			||||||
	if c == nil {
 | 
					 | 
				
			||||||
		return eris.Errorf("a non nil Configuration is mandatory")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if strings.TrimSpace(c.Table) != "" {
 | 
					 | 
				
			||||||
		mConfiguration.Table = c.Table
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	mConfiguration.Table = c.Table
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type afterMigrationComplete func()
 | 
					type afterMigrationComplete func()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Migrate will perform the migration from start to finish
 | 
					// Migrate will bring the db up to date
 | 
				
			||||||
func Migrate(followup afterMigrationComplete) bool {
 | 
					func Migrate(followup afterMigrationComplete) bool {
 | 
				
			||||||
	logger.Info("Migration: Started")
 | 
						dbURL := config.Configuration.DB.GetDBMateConnectURL()
 | 
				
			||||||
 | 
						u, _ := url.Parse(dbURL)
 | 
				
			||||||
 | 
						db := dbmate.New(u)
 | 
				
			||||||
 | 
						db.FS = embed.MigrationFiles
 | 
				
			||||||
 | 
						db.MigrationsDir = []string{fmt.Sprintf("./migrations/%s", config.Configuration.DB.GetDriver())}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Try to connect to the database sleeping for 15 seconds in between
 | 
						migrations, err := db.FindMigrations()
 | 
				
			||||||
	var db *sqlx.DB
 | 
					 | 
				
			||||||
	for {
 | 
					 | 
				
			||||||
		db = GetInstance()
 | 
					 | 
				
			||||||
		if db == nil {
 | 
					 | 
				
			||||||
			logger.Warn("Database is unavailable for migration, retrying in 15 seconds")
 | 
					 | 
				
			||||||
			time.Sleep(15 * time.Second)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check for migration table existence
 | 
					 | 
				
			||||||
	if !tableExists(db, mConfiguration.Table) {
 | 
					 | 
				
			||||||
		err := createMigrationTable(db)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
			logger.Error("MigratorError", err)
 | 
							logger.Error("MigrationError", err)
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		logger.Info("Migration: Migration Table created")
 | 
					
 | 
				
			||||||
 | 
						for _, m := range migrations {
 | 
				
			||||||
 | 
							logger.Debug("%s: %s", m.Version, m.FilePath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// DO MIGRATION
 | 
						err = db.CreateAndMigrate()
 | 
				
			||||||
	migrationCount, migrateErr := performFileMigrations(db)
 | 
						if err != nil {
 | 
				
			||||||
	if migrateErr != nil {
 | 
							logger.Error("MigrationError", err)
 | 
				
			||||||
		logger.Error("MigratorError", migrateErr)
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if migrateErr == nil {
 | 
					 | 
				
			||||||
		logger.Info("Migration: Completed %v migration files", migrationCount)
 | 
					 | 
				
			||||||
	followup()
 | 
						followup()
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// createMigrationTable performs a query to create the migration table
 | 
					 | 
				
			||||||
// with the name specified in the configuration
 | 
					 | 
				
			||||||
func createMigrationTable(db *sqlx.DB) error {
 | 
					 | 
				
			||||||
	logger.Info("Migration: Creating Migration Table: %v", mConfiguration.Table)
 | 
					 | 
				
			||||||
	// nolint:lll
 | 
					 | 
				
			||||||
	query := fmt.Sprintf("CREATE TABLE IF NOT EXISTS `%v` (filename TEXT PRIMARY KEY, migrated_on INTEGER NOT NULL DEFAULT 0)", mConfiguration.Table)
 | 
					 | 
				
			||||||
	_, err := db.Exec(query)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// tableExists will check the database for the existence of the specified table.
 | 
					 | 
				
			||||||
func tableExists(db *sqlx.DB, tableName string) bool {
 | 
					 | 
				
			||||||
	query := `SELECT CASE name WHEN $1 THEN true ELSE false END AS found FROM sqlite_master WHERE type='table' AND name = $1`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	row := db.QueryRowx(query, tableName)
 | 
					 | 
				
			||||||
	if row == nil {
 | 
					 | 
				
			||||||
		logger.Error("MigratorError", eris.Errorf("Cannot check if table exists, no row returned: %v", tableName))
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var exists *bool
 | 
					 | 
				
			||||||
	if err := row.Scan(&exists); err != nil {
 | 
					 | 
				
			||||||
		if err == sql.ErrNoRows {
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		logger.Error("MigratorError", err)
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return *exists
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// performFileMigrations will perform the actual migration,
 | 
					 | 
				
			||||||
// importing files and updating the database with the rows imported.
 | 
					 | 
				
			||||||
func performFileMigrations(db *sqlx.DB) (int, error) {
 | 
					 | 
				
			||||||
	var importedCount = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Grab a list of previously ran migrations from the database:
 | 
					 | 
				
			||||||
	previousMigrations, prevErr := getPreviousMigrations(db)
 | 
					 | 
				
			||||||
	if prevErr != nil {
 | 
					 | 
				
			||||||
		return importedCount, prevErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// List up the ".sql" files on disk
 | 
					 | 
				
			||||||
	err := fs.WalkDir(embed.MigrationFiles, ".", func(file string, d fs.DirEntry, err error) error {
 | 
					 | 
				
			||||||
		if !d.IsDir() {
 | 
					 | 
				
			||||||
			shortFile := filepath.Base(file)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Check if this file already exists in the previous migrations
 | 
					 | 
				
			||||||
			// and if so, ignore it
 | 
					 | 
				
			||||||
			if util.SliceContainsItem(previousMigrations, shortFile) {
 | 
					 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			logger.Info("Migration: Importing %v", shortFile)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			sqlContents, ioErr := embed.MigrationFiles.ReadFile(path.Clean(file))
 | 
					 | 
				
			||||||
			if ioErr != nil {
 | 
					 | 
				
			||||||
				return ioErr
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			sqlString := string(sqlContents)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			tx := db.MustBegin()
 | 
					 | 
				
			||||||
			if _, execErr := tx.Exec(sqlString); execErr != nil {
 | 
					 | 
				
			||||||
				return execErr
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if commitErr := tx.Commit(); commitErr != nil {
 | 
					 | 
				
			||||||
				return commitErr
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if markErr := markMigrationSuccessful(db, shortFile); markErr != nil {
 | 
					 | 
				
			||||||
				return markErr
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			importedCount++
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return importedCount, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getPreviousMigrations will query the migration table for names
 | 
					 | 
				
			||||||
// of migrations we can ignore because they should have already
 | 
					 | 
				
			||||||
// been imported
 | 
					 | 
				
			||||||
func getPreviousMigrations(db *sqlx.DB) ([]string, error) {
 | 
					 | 
				
			||||||
	var existingMigrations []string
 | 
					 | 
				
			||||||
	// nolint:gosec
 | 
					 | 
				
			||||||
	query := fmt.Sprintf("SELECT filename FROM `%v` ORDER BY filename", mConfiguration.Table)
 | 
					 | 
				
			||||||
	rows, err := db.Queryx(query)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if err == sql.ErrNoRows {
 | 
					 | 
				
			||||||
			return existingMigrations, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return existingMigrations, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for rows.Next() {
 | 
					 | 
				
			||||||
		var filename *string
 | 
					 | 
				
			||||||
		err := rows.Scan(&filename)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return existingMigrations, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		existingMigrations = append(existingMigrations, *filename)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return existingMigrations, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// markMigrationSuccessful will add a row to the migration table
 | 
					 | 
				
			||||||
func markMigrationSuccessful(db *sqlx.DB, filename string) error {
 | 
					 | 
				
			||||||
	// nolint:gosec
 | 
					 | 
				
			||||||
	query := fmt.Sprintf("INSERT INTO `%v` (filename) VALUES ($1)", mConfiguration.Table)
 | 
					 | 
				
			||||||
	_, err := db.Exec(query, filename)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,37 +0,0 @@
 | 
				
			|||||||
package database
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/config"
 | 
					 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CheckSetup Quick check by counting the number of users in the database
 | 
					 | 
				
			||||||
func CheckSetup() {
 | 
					 | 
				
			||||||
	query := `SELECT COUNT(*) FROM "user" WHERE is_deleted = $1 AND is_disabled = $1 AND is_system = $1`
 | 
					 | 
				
			||||||
	db := GetInstance()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if db != nil {
 | 
					 | 
				
			||||||
		row := db.QueryRowx(query, false)
 | 
					 | 
				
			||||||
		var totalRows int
 | 
					 | 
				
			||||||
		queryErr := row.Scan(&totalRows)
 | 
					 | 
				
			||||||
		if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
			logger.Error("SetupError", queryErr)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if totalRows == 0 {
 | 
					 | 
				
			||||||
			logger.Warn("No users found, starting in Setup Mode")
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			config.IsSetup = true
 | 
					 | 
				
			||||||
			logger.Info("Application is setup")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if config.ErrorReporting {
 | 
					 | 
				
			||||||
			logger.Warn("Error reporting is enabled - Application Errors WILL be sent to Sentry, you can disable this in the Settings interface")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		logger.Error("DatabaseError", errors.ErrDatabaseUnavailable)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,74 +0,0 @@
 | 
				
			|||||||
package database
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/config"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/jmoiron/sqlx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Blank import for Sqlite
 | 
					 | 
				
			||||||
	_ "modernc.org/sqlite"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var dbInstance *sqlx.DB
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewDB creates a new connection
 | 
					 | 
				
			||||||
func NewDB() {
 | 
					 | 
				
			||||||
	logger.Info("Creating new DB instance")
 | 
					 | 
				
			||||||
	db := SqliteDB()
 | 
					 | 
				
			||||||
	if db != nil {
 | 
					 | 
				
			||||||
		dbInstance = db
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetInstance returns an existing or new instance
 | 
					 | 
				
			||||||
func GetInstance() *sqlx.DB {
 | 
					 | 
				
			||||||
	if dbInstance == nil {
 | 
					 | 
				
			||||||
		NewDB()
 | 
					 | 
				
			||||||
	} else if err := dbInstance.Ping(); err != nil {
 | 
					 | 
				
			||||||
		NewDB()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return dbInstance
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SqliteDB Create sqlite client
 | 
					 | 
				
			||||||
func SqliteDB() *sqlx.DB {
 | 
					 | 
				
			||||||
	dbFile := fmt.Sprintf("%s/nginxproxymanager.db", config.Configuration.DataFolder)
 | 
					 | 
				
			||||||
	autocreate(dbFile)
 | 
					 | 
				
			||||||
	db, err := sqlx.Open("sqlite", dbFile)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("SqliteError", err)
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return db
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Commit will close and reopen the db file
 | 
					 | 
				
			||||||
func Commit() *sqlx.DB {
 | 
					 | 
				
			||||||
	if dbInstance != nil {
 | 
					 | 
				
			||||||
		err := dbInstance.Close()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			logger.Error("DatabaseCloseError", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	NewDB()
 | 
					 | 
				
			||||||
	return dbInstance
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func autocreate(dbFile string) {
 | 
					 | 
				
			||||||
	if _, err := os.Stat(dbFile); os.IsNotExist(err) {
 | 
					 | 
				
			||||||
		// Create it
 | 
					 | 
				
			||||||
		logger.Info("Creating Sqlite DB: %s", dbFile)
 | 
					 | 
				
			||||||
		// nolint: gosec
 | 
					 | 
				
			||||||
		_, err = os.Create(dbFile)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			logger.Error("FileCreateError", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		Commit()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package accesslist
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,122 +1,41 @@
 | 
				
			|||||||
package accesslist
 | 
					package accesslist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a row by ID
 | 
					// GetByID finds a row by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will create a row from this model
 | 
					 | 
				
			||||||
func Create(m *Model) (int, error) {
 | 
					 | 
				
			||||||
	if m.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create access list when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	m.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		user_id,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		meta,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:user_id,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:meta,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, m)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a row from this model
 | 
					 | 
				
			||||||
func Update(m *Model) error {
 | 
					 | 
				
			||||||
	if m.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update access list when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	m.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		user_id = :user_id,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		meta = :meta,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, m)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of access lists
 | 
					// List will return a list of access lists
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Error("ListAccessListsError", queryErr)
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("ListAccessListsError", err)
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,77 +1,54 @@
 | 
				
			|||||||
package accesslist
 | 
					package accesslist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/entity/user"
 | 
						"npm/internal/entity/user"
 | 
				
			||||||
	"npm/internal/types"
 | 
						"npm/internal/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "access_list"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the access list model
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID         int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn  types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						UserID int         `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Name   string      `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	UserID     int          `json:"user_id" db:"user_id" filter:"user_id,integer"`
 | 
						Meta   types.JSONB `json:"meta" gorm:"column:meta"`
 | 
				
			||||||
	Name       string       `json:"name" db:"name" filter:"name,string"`
 | 
					 | 
				
			||||||
	Meta       types.JSONB  `json:"meta" db:"meta"`
 | 
					 | 
				
			||||||
	IsDeleted  bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
	// Expansions
 | 
						// Expansions
 | 
				
			||||||
	User *user.Model `json:"user,omitempty"`
 | 
						User *user.Model `json:"user,omitempty" gorm:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "access_list"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.UserID == 0 {
 | 
						if m.UserID == 0 {
 | 
				
			||||||
		return eris.Errorf("User ID must be specified")
 | 
							return eris.Errorf("User ID must be specified")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						result := db.Save(m)
 | 
				
			||||||
	} else {
 | 
						return result.Error
 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a access list as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package accesslist
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for the list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,11 +1,7 @@
 | 
				
			|||||||
package auth
 | 
					package auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a auth by ID
 | 
					// GetByID finds a auth by ID
 | 
				
			||||||
@@ -16,12 +12,17 @@ func GetByID(id int) (Model, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByUserIDType finds a user by email
 | 
					// GetByUserIDType finds a user by email
 | 
				
			||||||
func GetByUserIDType(userID int, authType string) (Model, error) {
 | 
					func GetByUserIDType(userID uint, authType string) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var auth Model
 | 
				
			||||||
	err := m.LoadByUserIDType(userID, authType)
 | 
						db := database.GetDB()
 | 
				
			||||||
	return m, err
 | 
						result := db.
 | 
				
			||||||
 | 
							Where("user_id = ?", userID).
 | 
				
			||||||
 | 
							Where("type = ?", authType).
 | 
				
			||||||
 | 
							First(&auth)
 | 
				
			||||||
 | 
						return auth, result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
// Create will create a Auth from this model
 | 
					// Create will create a Auth from this model
 | 
				
			||||||
func Create(auth *Model) (int, error) {
 | 
					func Create(auth *Model) (int, error) {
 | 
				
			||||||
	if auth.ID != 0 {
 | 
						if auth.ID != 0 {
 | 
				
			||||||
@@ -59,7 +60,9 @@ func Create(auth *Model) (int, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return int(last), nil
 | 
						return int(last), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
// Update will Update a Auth from this model
 | 
					// Update will Update a Auth from this model
 | 
				
			||||||
func Update(auth *Model) error {
 | 
					func Update(auth *Model) error {
 | 
				
			||||||
	if auth.ID == 0 {
 | 
						if auth.ID == 0 {
 | 
				
			||||||
@@ -81,3 +84,4 @@ func Update(auth *Model) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,52 +1,49 @@
 | 
				
			|||||||
package auth
 | 
					package auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/types"
 | 
						"npm/internal/entity"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
	"golang.org/x/crypto/bcrypt"
 | 
						"golang.org/x/crypto/bcrypt"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	tableName = "auth"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TypePassword is the Password Type
 | 
						// TypePassword is the Password Type
 | 
				
			||||||
	TypePassword = "password"
 | 
						TypePassword = "password"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Model is the user model
 | 
					// Model is the model
 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID         int          `json:"id" db:"id"`
 | 
						entity.ModelBase
 | 
				
			||||||
	UserID     int          `json:"user_id" db:"user_id"`
 | 
						UserID uint   `json:"user_id" gorm:"column:user_id"`
 | 
				
			||||||
	Type       string       `json:"type" db:"type"`
 | 
						Type   string `json:"type" gorm:"column:type;default:password"`
 | 
				
			||||||
	Secret     string       `json:"secret,omitempty" db:"secret"`
 | 
						Secret string `json:"secret,omitempty" gorm:"column:secret"`
 | 
				
			||||||
	CreatedOn  types.DBDate `json:"created_on" db:"created_on"`
 | 
					 | 
				
			||||||
	ModifiedOn types.DBDate `json:"modified_on" db:"modified_on"`
 | 
					 | 
				
			||||||
	IsDeleted  bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "auth"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id int) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByUserIDType will load from an ID
 | 
					// LoadByUserIDType will load from an ID
 | 
				
			||||||
func (m *Model) LoadByUserIDType(userID int, authType string) error {
 | 
					func (m *Model) LoadByUserIDType(userID int, authType string) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE user_id = ? AND type = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{userID, authType}
 | 
						result := db.
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
							Where("user_id = ?", userID).
 | 
				
			||||||
 | 
							Where("type = ?", authType).
 | 
				
			||||||
 | 
							First(&m)
 | 
				
			||||||
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					// Touch will update model's timestamp(s)
 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					func (m *Model) Touch(created bool) {
 | 
				
			||||||
	var d types.DBDate
 | 
						var d types.DBDate
 | 
				
			||||||
@@ -56,18 +53,14 @@ func (m *Model) Touch(created bool) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	m.ModifiedOn = d
 | 
						m.ModifiedOn = d
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						// todo: touch? not sure that save does this or not?
 | 
				
			||||||
	if m.ID == 0 {
 | 
						result := db.Save(m)
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						return result.Error
 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetPassword will generate a hashed password based on given string
 | 
					// SetPassword will generate a hashed password based on given string
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								backend/internal/entity/capability.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								backend/internal/entity/capability.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Capability is the db model
 | 
				
			||||||
 | 
					type Capability struct {
 | 
				
			||||||
 | 
						Name string `json:"name" gorm:"column:name;primaryKey" filter:"name,string"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
 | 
					func (Capability) TableName() string {
 | 
				
			||||||
 | 
						return "capability"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package certificate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,142 +1,54 @@
 | 
				
			|||||||
package certificate
 | 
					package certificate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/jobqueue"
 | 
						"npm/internal/jobqueue"
 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a row by ID
 | 
					// GetByID finds a row by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will create a row from this model
 | 
					// GetByStatus will select rows that are ready for requesting
 | 
				
			||||||
func Create(certificate *Model) (int, error) {
 | 
					func GetByStatus(status string) ([]Model, error) {
 | 
				
			||||||
	if certificate.ID != 0 {
 | 
						items := make([]Model, 0)
 | 
				
			||||||
		return 0, eris.New("Cannot create certificate when model already has an ID")
 | 
						db := database.GetDB()
 | 
				
			||||||
	}
 | 
						result := db.
 | 
				
			||||||
 | 
							Joins("INNER JOIN certificate_authority ON certificate_authority.id = certificate.certificate_authority_id AND certificate_authority.is_deleted = ?", 0).
 | 
				
			||||||
	certificate.Touch(true)
 | 
							Where("type IN ?", []string{"http", "dns"}).
 | 
				
			||||||
 | 
							Where("status = ?", status).
 | 
				
			||||||
	db := database.GetInstance()
 | 
							Where("certificate_authority_id > ?", 0).
 | 
				
			||||||
	// nolint: gosec
 | 
							Find(&items)
 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
						return items, result.Error
 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		user_id,
 | 
					 | 
				
			||||||
		type,
 | 
					 | 
				
			||||||
		certificate_authority_id,
 | 
					 | 
				
			||||||
		dns_provider_id,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		domain_names,
 | 
					 | 
				
			||||||
		expires_on,
 | 
					 | 
				
			||||||
		status,
 | 
					 | 
				
			||||||
		error_message,
 | 
					 | 
				
			||||||
		meta,
 | 
					 | 
				
			||||||
		is_ecc,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:user_id,
 | 
					 | 
				
			||||||
		:type,
 | 
					 | 
				
			||||||
		:certificate_authority_id,
 | 
					 | 
				
			||||||
		:dns_provider_id,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:domain_names,
 | 
					 | 
				
			||||||
		:expires_on,
 | 
					 | 
				
			||||||
		:status,
 | 
					 | 
				
			||||||
		:error_message,
 | 
					 | 
				
			||||||
		:meta,
 | 
					 | 
				
			||||||
		:is_ecc,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, certificate)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a Auth from this model
 | 
					 | 
				
			||||||
func Update(certificate *Model) error {
 | 
					 | 
				
			||||||
	if certificate.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update certificate when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	certificate.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		type = :type,
 | 
					 | 
				
			||||||
		user_id = :user_id,
 | 
					 | 
				
			||||||
		certificate_authority_id = :certificate_authority_id,
 | 
					 | 
				
			||||||
		dns_provider_id = :dns_provider_id,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		domain_names = :domain_names,
 | 
					 | 
				
			||||||
		expires_on = :expires_on,
 | 
					 | 
				
			||||||
		status = :status,
 | 
					 | 
				
			||||||
		error_message = :error_message,
 | 
					 | 
				
			||||||
		meta = :meta,
 | 
					 | 
				
			||||||
		is_ecc = :is_ecc,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, certificate)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List will return a list of certificates
 | 
					// List will return a list of certificates
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if expand != nil {
 | 
						if expand != nil {
 | 
				
			||||||
@@ -148,7 +60,7 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (Lis
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
@@ -160,33 +72,6 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (Lis
 | 
				
			|||||||
	return result, nil
 | 
						return result, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByStatus will select rows that are ready for requesting
 | 
					 | 
				
			||||||
func GetByStatus(status string) ([]Model, error) {
 | 
					 | 
				
			||||||
	models := make([]Model, 0)
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	query := fmt.Sprintf(`
 | 
					 | 
				
			||||||
	SELECT
 | 
					 | 
				
			||||||
		t.*
 | 
					 | 
				
			||||||
	FROM "%s" t
 | 
					 | 
				
			||||||
	INNER JOIN "certificate_authority" c ON c."id" = t."certificate_authority_id"
 | 
					 | 
				
			||||||
	WHERE
 | 
					 | 
				
			||||||
		t."type" IN ("http", "dns") AND
 | 
					 | 
				
			||||||
		t."status" = ? AND
 | 
					 | 
				
			||||||
		t."certificate_authority_id" > 0 AND
 | 
					 | 
				
			||||||
		t."is_deleted" = 0
 | 
					 | 
				
			||||||
	`, tableName)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	params := []interface{}{StatusReady}
 | 
					 | 
				
			||||||
	err := db.Select(&models, query, params...)
 | 
					 | 
				
			||||||
	if err != nil && err != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Error("GetByStatusError", err)
 | 
					 | 
				
			||||||
		logger.Debug("Query: %s -- %+v", query, params)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return models, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AddPendingJobs is intended to be used at startup to add
 | 
					// AddPendingJobs is intended to be used at startup to add
 | 
				
			||||||
// anything pending to the JobQueue just once, based on
 | 
					// anything pending to the JobQueue just once, based on
 | 
				
			||||||
// the database row status
 | 
					// the database row status
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"npm/internal/acme"
 | 
						"npm/internal/acme"
 | 
				
			||||||
	"npm/internal/config"
 | 
						"npm/internal/config"
 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/entity/certificateauthority"
 | 
						"npm/internal/entity/certificateauthority"
 | 
				
			||||||
	"npm/internal/entity/dnsprovider"
 | 
						"npm/internal/entity/dnsprovider"
 | 
				
			||||||
	"npm/internal/entity/user"
 | 
						"npm/internal/entity/user"
 | 
				
			||||||
@@ -22,8 +23,6 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	tableName = "certificate"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TypeCustom custom cert type
 | 
						// TypeCustom custom cert type
 | 
				
			||||||
	TypeCustom = "custom"
 | 
						TypeCustom = "custom"
 | 
				
			||||||
	// TypeHTTP http cert type
 | 
						// TypeHTTP http cert type
 | 
				
			||||||
@@ -43,54 +42,40 @@ const (
 | 
				
			|||||||
	StatusProvided = "provided"
 | 
						StatusProvided = "provided"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Model is the user model
 | 
					// Model is the model
 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID                     int                  `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn              types.DBDate         `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						ExpiresOn              types.NullableDBDate `json:"expires_on" gorm:"column:expires_on" filter:"expires_on,integer"`
 | 
				
			||||||
	ModifiedOn             types.DBDate         `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Type                   string               `json:"type" gorm:"column:type" filter:"type,string"`
 | 
				
			||||||
	ExpiresOn              types.NullableDBDate `json:"expires_on" db:"expires_on" filter:"expires_on,integer"`
 | 
						UserID                 uint                 `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	Type                   string               `json:"type" db:"type" filter:"type,string"`
 | 
						CertificateAuthorityID uint                 `json:"certificate_authority_id" gorm:"column:certificate_authority_id" filter:"certificate_authority_id,integer"`
 | 
				
			||||||
	UserID                 int                  `json:"user_id" db:"user_id" filter:"user_id,integer"`
 | 
						DNSProviderID          uint                 `json:"dns_provider_id" gorm:"column:dns_provider_id" filter:"dns_provider_id,integer"`
 | 
				
			||||||
	CertificateAuthorityID int                  `json:"certificate_authority_id" db:"certificate_authority_id" filter:"certificate_authority_id,integer"`
 | 
						Name                   string               `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	DNSProviderID          int                  `json:"dns_provider_id" db:"dns_provider_id" filter:"dns_provider_id,integer"`
 | 
						DomainNames            types.JSONB          `json:"domain_names" gorm:"column:domain_names" filter:"domain_names,string"`
 | 
				
			||||||
	Name                   string               `json:"name" db:"name" filter:"name,string"`
 | 
						Status                 string               `json:"status" gorm:"column:status" filter:"status,string"`
 | 
				
			||||||
	DomainNames            types.JSONB          `json:"domain_names" db:"domain_names" filter:"domain_names,string"`
 | 
						ErrorMessage           string               `json:"error_message" gorm:"column:error_message" filter:"error_message,string"`
 | 
				
			||||||
	Status                 string               `json:"status" db:"status" filter:"status,string"`
 | 
						Meta                   types.JSONB          `json:"-" gorm:"column:meta"`
 | 
				
			||||||
	ErrorMessage           string               `json:"error_message" db:"error_message" filter:"error_message,string"`
 | 
						IsECC                  bool                 `json:"is_ecc" gorm:"column:is_ecc" filter:"is_ecc,bool"`
 | 
				
			||||||
	Meta                   types.JSONB          `json:"-" db:"meta"`
 | 
					 | 
				
			||||||
	IsECC                  bool                 `json:"is_ecc" db:"is_ecc" filter:"is_ecc,bool"`
 | 
					 | 
				
			||||||
	IsDeleted              bool                 `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
	// Expansions:
 | 
						// Expansions:
 | 
				
			||||||
	CertificateAuthority *certificateauthority.Model `json:"certificate_authority,omitempty"`
 | 
						CertificateAuthority *certificateauthority.Model `json:"certificate_authority,omitempty" gorm:"-"`
 | 
				
			||||||
	DNSProvider          *dnsprovider.Model          `json:"dns_provider,omitempty"`
 | 
						DNSProvider          *dnsprovider.Model          `json:"dns_provider,omitempty" gorm:"-"`
 | 
				
			||||||
	User                 *user.Model                 `json:"user,omitempty"`
 | 
						User                 *user.Model                 `json:"user,omitempty" gorm:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "certificate"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.UserID == 0 {
 | 
						if m.UserID == 0 {
 | 
				
			||||||
		return eris.Errorf("User ID must be specified")
 | 
							return eris.Errorf("User ID must be specified")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -108,26 +93,22 @@ func (m *Model) Save() error {
 | 
				
			|||||||
	// ensure name is trimmed of whitespace
 | 
						// ensure name is trimmed of whitespace
 | 
				
			||||||
	m.Name = strings.TrimSpace(m.Name)
 | 
						m.Name = strings.TrimSpace(m.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						result := db.Save(m)
 | 
				
			||||||
	} else {
 | 
						return result.Error
 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a certificate as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// todo: delete from acme.sh as well
 | 
						// todo: delete from acme.sh as well
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate will make sure the data given is expected. This object is a bit complicated,
 | 
					// Validate will make sure the data given is expected. This object is a bit complicated,
 | 
				
			||||||
@@ -304,8 +285,8 @@ func (m *Model) GetTemplate() Template {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return Template{
 | 
						return Template{
 | 
				
			||||||
		ID:                     m.ID,
 | 
							ID:                     m.ID,
 | 
				
			||||||
		CreatedOn:              m.CreatedOn.Time.String(),
 | 
							CreatedAt:              fmt.Sprintf("%d", m.CreatedAt), // todo: nice date string
 | 
				
			||||||
		ModifiedOn:             m.ModifiedOn.Time.String(),
 | 
							UpdatedAt:              fmt.Sprintf("%d", m.UpdatedAt), // todo: nice date string
 | 
				
			||||||
		ExpiresOn:              m.ExpiresOn.AsString(),
 | 
							ExpiresOn:              m.ExpiresOn.AsString(),
 | 
				
			||||||
		Type:                   m.Type,
 | 
							Type:                   m.Type,
 | 
				
			||||||
		UserID:                 m.UserID,
 | 
							UserID:                 m.UserID,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package certificate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for users list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -2,14 +2,14 @@ package certificate
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Template is the model given to the template parser, converted from the Model
 | 
					// Template is the model given to the template parser, converted from the Model
 | 
				
			||||||
type Template struct {
 | 
					type Template struct {
 | 
				
			||||||
	ID                     int
 | 
						ID                     uint
 | 
				
			||||||
	CreatedOn              string
 | 
						CreatedAt              string
 | 
				
			||||||
	ModifiedOn             string
 | 
						UpdatedAt              string
 | 
				
			||||||
	ExpiresOn              string
 | 
						ExpiresOn              string
 | 
				
			||||||
	Type                   string
 | 
						Type                   string
 | 
				
			||||||
	UserID                 int
 | 
						UserID                 uint
 | 
				
			||||||
	CertificateAuthorityID int
 | 
						CertificateAuthorityID uint
 | 
				
			||||||
	DNSProviderID          int
 | 
						DNSProviderID          uint
 | 
				
			||||||
	Name                   string
 | 
						Name                   string
 | 
				
			||||||
	DomainNames            []string
 | 
						DomainNames            []string
 | 
				
			||||||
	Status                 string
 | 
						Status                 string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package certificateauthority
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,128 +1,41 @@
 | 
				
			|||||||
package certificateauthority
 | 
					package certificateauthority
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a row by ID
 | 
					// GetByID finds a row by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will create a row from this model
 | 
					 | 
				
			||||||
func Create(ca *Model) (int, error) {
 | 
					 | 
				
			||||||
	if ca.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create certificate authority when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ca.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		acmesh_server,
 | 
					 | 
				
			||||||
		ca_bundle,
 | 
					 | 
				
			||||||
		max_domains,
 | 
					 | 
				
			||||||
		is_wildcard_supported,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:acmesh_server,
 | 
					 | 
				
			||||||
		:ca_bundle,
 | 
					 | 
				
			||||||
		:max_domains,
 | 
					 | 
				
			||||||
		:is_wildcard_supported,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, ca)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a row from this model
 | 
					 | 
				
			||||||
func Update(ca *Model) error {
 | 
					 | 
				
			||||||
	if ca.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update certificate authority when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ca.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		acmesh_server = :acmesh_server,
 | 
					 | 
				
			||||||
		ca_bundle = :ca_bundle,
 | 
					 | 
				
			||||||
		max_domains = :max_domains,
 | 
					 | 
				
			||||||
		is_wildcard_supported = :is_wildcard_supported,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, ca)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of certificates
 | 
					// List will return a list of certificates
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Error("ListCertificateAuthoritiesError", queryErr)
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("ListCertificateAuthoritiesError", err)
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,78 +1,55 @@
 | 
				
			|||||||
package certificateauthority
 | 
					package certificateauthority
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
						"npm/internal/errors"
 | 
				
			||||||
	"npm/internal/types"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "certificate_authority"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the user model
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID                  int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn           types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						Name                string `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	ModifiedOn          types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						AcmeshServer        string `json:"acmesh_server" gorm:"column:acmesh_server" filter:"acmesh_server,string"`
 | 
				
			||||||
	Name                string       `json:"name" db:"name" filter:"name,string"`
 | 
						CABundle            string `json:"ca_bundle" gorm:"column:ca_bundle" filter:"ca_bundle,string"`
 | 
				
			||||||
	AcmeshServer        string       `json:"acmesh_server" db:"acmesh_server" filter:"acmesh_server,string"`
 | 
						MaxDomains          int    `json:"max_domains" gorm:"column:max_domains" filter:"max_domains,integer"`
 | 
				
			||||||
	CABundle            string       `json:"ca_bundle" db:"ca_bundle" filter:"ca_bundle,string"`
 | 
						IsWildcardSupported bool   `json:"is_wildcard_supported" gorm:"column:is_wildcard_supported" filter:"is_wildcard_supported,boolean"`
 | 
				
			||||||
	MaxDomains          int          `json:"max_domains" db:"max_domains" filter:"max_domains,integer"`
 | 
						IsReadonly          bool   `json:"is_readonly" gorm:"column:is_readonly" filter:"is_readonly,boolean"`
 | 
				
			||||||
	IsWildcardSupported bool         `json:"is_wildcard_supported" db:"is_wildcard_supported" filter:"is_wildcard_supported,boolean"`
 | 
					 | 
				
			||||||
	IsReadonly          bool         `json:"is_readonly" db:"is_readonly" filter:"is_readonly,boolean"`
 | 
					 | 
				
			||||||
	IsDeleted           bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "certificate_authority"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Save(m)
 | 
				
			||||||
	if m.ID == 0 {
 | 
						return result.Error
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a certificate as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Check will ensure the ca bundle path exists if it's set
 | 
					// Check will ensure the ca bundle path exists if it's set
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package certificateauthority
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for users list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package dnsprovider
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,128 +1,41 @@
 | 
				
			|||||||
package dnsprovider
 | 
					package dnsprovider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a row by ID
 | 
					// GetByID finds a row by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will create a row from this model
 | 
					 | 
				
			||||||
func Create(provider *Model) (int, error) {
 | 
					 | 
				
			||||||
	if provider.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create dns provider when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	provider.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		user_id,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		acmesh_name,
 | 
					 | 
				
			||||||
		dns_sleep,
 | 
					 | 
				
			||||||
		meta,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:user_id,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:acmesh_name,
 | 
					 | 
				
			||||||
		:dns_sleep,
 | 
					 | 
				
			||||||
		:meta,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, provider)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a row from this model
 | 
					 | 
				
			||||||
func Update(provider *Model) error {
 | 
					 | 
				
			||||||
	if provider.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update dns provider when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	provider.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		user_id = :user_id,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		acmesh_name = :acmesh_name,
 | 
					 | 
				
			||||||
		dns_sleep = :dns_sleep,
 | 
					 | 
				
			||||||
		meta = :meta,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, provider)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of certificates
 | 
					// List will return a list of certificates
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Error("ListDnsProvidersError", queryErr)
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("ListDnsProvidersError", err)
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,80 +2,58 @@ package dnsprovider
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/dnsproviders"
 | 
						"npm/internal/dnsproviders"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
	"npm/internal/types"
 | 
						"npm/internal/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "dns_provider"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the user model
 | 
					 | 
				
			||||||
// Also see: https://github.com/acmesh-official/acme.sh/wiki/dnscheck
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID         int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn  types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						UserID     int         `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Name       string      `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	UserID     int          `json:"user_id" db:"user_id" filter:"user_id,integer"`
 | 
						AcmeshName string      `json:"acmesh_name" gorm:"column:acmesh_name" filter:"acmesh_name,string"`
 | 
				
			||||||
	Name       string       `json:"name" db:"name" filter:"name,string"`
 | 
						DNSSleep   int         `json:"dns_sleep" gorm:"column:dns_sleep" filter:"dns_sleep,integer"`
 | 
				
			||||||
	AcmeshName string       `json:"acmesh_name" db:"acmesh_name" filter:"acmesh_name,string"`
 | 
						Meta       types.JSONB `json:"meta" gorm:"column:meta"`
 | 
				
			||||||
	DNSSleep   int          `json:"dns_sleep" db:"dns_sleep" filter:"dns_sleep,integer"`
 | 
					 | 
				
			||||||
	Meta       types.JSONB  `json:"meta" db:"meta"`
 | 
					 | 
				
			||||||
	IsDeleted  bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "dns_provider"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.UserID == 0 {
 | 
						if m.UserID == 0 {
 | 
				
			||||||
		return eris.Errorf("User ID must be specified")
 | 
							return eris.Errorf("User ID must be specified")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						result := db.Save(m)
 | 
				
			||||||
	} else {
 | 
						return result.Error
 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a certificate as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetAcmeShEnvVars returns the env vars required for acme.sh dns cert requests
 | 
					// GetAcmeShEnvVars returns the env vars required for acme.sh dns cert requests
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package dnsprovider
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for the list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -11,12 +11,6 @@ import (
 | 
				
			|||||||
// FilterMapFunction is a filter map function
 | 
					// FilterMapFunction is a filter map function
 | 
				
			||||||
type FilterMapFunction func(value []string) []string
 | 
					type FilterMapFunction func(value []string) []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FilterTagName tag name user for filter pickups
 | 
					 | 
				
			||||||
const FilterTagName = "filter"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DBTagName tag name user for field name pickups
 | 
					 | 
				
			||||||
const DBTagName = "db"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GenerateSQLFromFilters will return a Query and params for use as WHERE clause in SQL queries
 | 
					// GenerateSQLFromFilters will return a Query and params for use as WHERE clause in SQL queries
 | 
				
			||||||
// This will use a AND where clause approach.
 | 
					// This will use a AND where clause approach.
 | 
				
			||||||
func GenerateSQLFromFilters(filters []model.Filter, fieldMap map[string]string, fieldMapFunctions map[string]FilterMapFunction) (string, []interface{}) {
 | 
					func GenerateSQLFromFilters(filters []model.Filter, fieldMap map[string]string, fieldMapFunctions map[string]FilterMapFunction) (string, []interface{}) {
 | 
				
			||||||
@@ -96,6 +90,7 @@ func getSQLAssignmentFromModifier(filter model.Filter, params *[]interface{}) st
 | 
				
			|||||||
	return clause
 | 
						return clause
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
// GetFilterMap returns the filter map
 | 
					// GetFilterMap returns the filter map
 | 
				
			||||||
func GetFilterMap(m interface{}) map[string]string {
 | 
					func GetFilterMap(m interface{}) map[string]string {
 | 
				
			||||||
	var filterMap = make(map[string]string)
 | 
						var filterMap = make(map[string]string)
 | 
				
			||||||
@@ -110,8 +105,8 @@ func GetFilterMap(m interface{}) map[string]string {
 | 
				
			|||||||
		field := t.Field(i)
 | 
							field := t.Field(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Get the field tag value
 | 
							// Get the field tag value
 | 
				
			||||||
		filterTag := field.Tag.Get(FilterTagName)
 | 
							filterTag := field.Tag.Get("filter")
 | 
				
			||||||
		dbTag := field.Tag.Get(DBTagName)
 | 
							dbTag := field.Tag.Get("db")
 | 
				
			||||||
		if filterTag != "" && dbTag != "" && dbTag != "-" && filterTag != "-" {
 | 
							if filterTag != "" && dbTag != "" && dbTag != "-" && filterTag != "-" {
 | 
				
			||||||
			// Filter tag can be a 2 part thing: name,type
 | 
								// Filter tag can be a 2 part thing: name,type
 | 
				
			||||||
			// ie: account_id,integer
 | 
								// ie: account_id,integer
 | 
				
			||||||
@@ -124,6 +119,7 @@ func GetFilterMap(m interface{}) map[string]string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return filterMap
 | 
						return filterMap
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetDBColumns returns the db columns
 | 
					// GetDBColumns returns the db columns
 | 
				
			||||||
func GetDBColumns(m interface{}) []string {
 | 
					func GetDBColumns(m interface{}) []string {
 | 
				
			||||||
@@ -132,7 +128,7 @@ func GetDBColumns(m interface{}) []string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < t.NumField(); i++ {
 | 
						for i := 0; i < t.NumField(); i++ {
 | 
				
			||||||
		field := t.Field(i)
 | 
							field := t.Field(i)
 | 
				
			||||||
		dbTag := field.Tag.Get(DBTagName)
 | 
							dbTag := field.Tag.Get("db")
 | 
				
			||||||
		if dbTag != "" && dbTag != "-" {
 | 
							if dbTag != "" && dbTag != "-" {
 | 
				
			||||||
			columns = append(columns, dbTag)
 | 
								columns = append(columns, dbTag)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ func GetFilterSchema(m interface{}) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < t.NumField(); i++ {
 | 
						for i := 0; i < t.NumField(); i++ {
 | 
				
			||||||
		field := t.Field(i)
 | 
							field := t.Field(i)
 | 
				
			||||||
		filterTag := field.Tag.Get(FilterTagName)
 | 
							filterTag := field.Tag.Get("filter")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if filterTag != "" && filterTag != "-" {
 | 
							if filterTag != "" && filterTag != "-" {
 | 
				
			||||||
			// split out tag value "field,filtreType"
 | 
								// split out tag value "field,filtreType"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package host
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,181 +1,40 @@
 | 
				
			|||||||
package host
 | 
					package host
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a Host by ID
 | 
					// GetByID finds a Host by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// create will create a Host from this model
 | 
					 | 
				
			||||||
func create(host *Model) (int, error) {
 | 
					 | 
				
			||||||
	if host.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create host when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	host.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+tableName+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		user_id,
 | 
					 | 
				
			||||||
		type,
 | 
					 | 
				
			||||||
		nginx_template_id,
 | 
					 | 
				
			||||||
		listen_interface,
 | 
					 | 
				
			||||||
		domain_names,
 | 
					 | 
				
			||||||
		upstream_id,
 | 
					 | 
				
			||||||
		proxy_scheme,
 | 
					 | 
				
			||||||
		proxy_host,
 | 
					 | 
				
			||||||
		proxy_port,
 | 
					 | 
				
			||||||
		certificate_id,
 | 
					 | 
				
			||||||
		access_list_id,
 | 
					 | 
				
			||||||
		ssl_forced,
 | 
					 | 
				
			||||||
		caching_enabled,
 | 
					 | 
				
			||||||
		block_exploits,
 | 
					 | 
				
			||||||
		allow_websocket_upgrade,
 | 
					 | 
				
			||||||
		http2_support,
 | 
					 | 
				
			||||||
		hsts_enabled,
 | 
					 | 
				
			||||||
		hsts_subdomains,
 | 
					 | 
				
			||||||
		paths,
 | 
					 | 
				
			||||||
		advanced_config,
 | 
					 | 
				
			||||||
		status,
 | 
					 | 
				
			||||||
		error_message,
 | 
					 | 
				
			||||||
		is_disabled,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:user_id,
 | 
					 | 
				
			||||||
		:type,
 | 
					 | 
				
			||||||
		:nginx_template_id,
 | 
					 | 
				
			||||||
		:listen_interface,
 | 
					 | 
				
			||||||
		:domain_names,
 | 
					 | 
				
			||||||
		:upstream_id,
 | 
					 | 
				
			||||||
		:proxy_scheme,
 | 
					 | 
				
			||||||
		:proxy_host,
 | 
					 | 
				
			||||||
		:proxy_port,
 | 
					 | 
				
			||||||
		:certificate_id,
 | 
					 | 
				
			||||||
		:access_list_id,
 | 
					 | 
				
			||||||
		:ssl_forced,
 | 
					 | 
				
			||||||
		:caching_enabled,
 | 
					 | 
				
			||||||
		:block_exploits,
 | 
					 | 
				
			||||||
		:allow_websocket_upgrade,
 | 
					 | 
				
			||||||
		:http2_support,
 | 
					 | 
				
			||||||
		:hsts_enabled,
 | 
					 | 
				
			||||||
		:hsts_subdomains,
 | 
					 | 
				
			||||||
		:paths,
 | 
					 | 
				
			||||||
		:advanced_config,
 | 
					 | 
				
			||||||
		:status,
 | 
					 | 
				
			||||||
		:error_message,
 | 
					 | 
				
			||||||
		:is_disabled,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logger.Debug("Created Host: %+v", host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// update will Update a Host from this model
 | 
					 | 
				
			||||||
func update(host *Model) error {
 | 
					 | 
				
			||||||
	if host.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update host when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	host.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		user_id = :user_id,
 | 
					 | 
				
			||||||
		type = :type,
 | 
					 | 
				
			||||||
		nginx_template_id = :nginx_template_id,
 | 
					 | 
				
			||||||
		listen_interface = :listen_interface,
 | 
					 | 
				
			||||||
		domain_names = :domain_names,
 | 
					 | 
				
			||||||
		upstream_id = :upstream_id,
 | 
					 | 
				
			||||||
		proxy_scheme = :proxy_scheme,
 | 
					 | 
				
			||||||
		proxy_host = :proxy_host,
 | 
					 | 
				
			||||||
		proxy_port = :proxy_port,
 | 
					 | 
				
			||||||
		certificate_id = :certificate_id,
 | 
					 | 
				
			||||||
		access_list_id = :access_list_id,
 | 
					 | 
				
			||||||
		ssl_forced = :ssl_forced,
 | 
					 | 
				
			||||||
		caching_enabled = :caching_enabled,
 | 
					 | 
				
			||||||
		block_exploits = :block_exploits,
 | 
					 | 
				
			||||||
		allow_websocket_upgrade = :allow_websocket_upgrade,
 | 
					 | 
				
			||||||
		http2_support = :http2_support,
 | 
					 | 
				
			||||||
		hsts_enabled = :hsts_enabled,
 | 
					 | 
				
			||||||
		hsts_subdomains = :hsts_subdomains,
 | 
					 | 
				
			||||||
		paths = :paths,
 | 
					 | 
				
			||||||
		advanced_config = :advanced_config,
 | 
					 | 
				
			||||||
		status = :status,
 | 
					 | 
				
			||||||
		error_message = :error_message,
 | 
					 | 
				
			||||||
		is_disabled = :is_disabled,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logger.Debug("Updated Host: %+v", host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of hosts
 | 
					// List will return a list of hosts
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "domain_names",
 | 
							Field:     "domain_names",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if expand != nil {
 | 
						if expand != nil {
 | 
				
			||||||
@@ -187,7 +46,7 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (Lis
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
@@ -201,32 +60,28 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (Lis
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetUpstreamUseCount returns the number of hosts that are using
 | 
					// GetUpstreamUseCount returns the number of hosts that are using
 | 
				
			||||||
// an upstream, and have not been deleted.
 | 
					// an upstream, and have not been deleted.
 | 
				
			||||||
func GetUpstreamUseCount(upstreamID int) int {
 | 
					func GetUpstreamUseCount(upstreamID uint) int64 {
 | 
				
			||||||
	db := database.GetInstance()
 | 
						db := database.GetDB()
 | 
				
			||||||
	query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE upstream_id = ? AND is_deleted = ?", tableName)
 | 
					
 | 
				
			||||||
	countRow := db.QueryRowx(query, upstreamID, 0)
 | 
						var count int64
 | 
				
			||||||
	var totalRows int
 | 
						if result := db.Model(&Model{}).Where("upstream_id = ?", upstreamID).Count(&count); result.Error != nil {
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
							logger.Debug("GetUpstreamUseCount Error: %v", result.Error)
 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s", query)
 | 
					 | 
				
			||||||
		return 0
 | 
							return 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return totalRows
 | 
						return count
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCertificateUseCount returns the number of hosts that are using
 | 
					// GetCertificateUseCount returns the number of hosts that are using
 | 
				
			||||||
// a certificate, and have not been deleted.
 | 
					// a certificate, and have not been deleted.
 | 
				
			||||||
func GetCertificateUseCount(certificateID int) int {
 | 
					func GetCertificateUseCount(certificateID uint) int64 {
 | 
				
			||||||
	db := database.GetInstance()
 | 
						db := database.GetDB()
 | 
				
			||||||
	query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE certificate_id = ? AND is_deleted = ?", tableName)
 | 
					
 | 
				
			||||||
	countRow := db.QueryRowx(query, certificateID, 0)
 | 
						var count int64
 | 
				
			||||||
	var totalRows int
 | 
						if result := db.Model(&Model{}).Where("certificate_id = ?", certificateID).Count(&count); result.Error != nil {
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
							logger.Debug("GetUpstreamUseCount Error: %v", result.Error)
 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s", query)
 | 
					 | 
				
			||||||
		return 0
 | 
							return 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return totalRows
 | 
						return count
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddPendingJobs is intended to be used at startup to add
 | 
					// AddPendingJobs is intended to be used at startup to add
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,8 @@ package host
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/entity/certificate"
 | 
						"npm/internal/entity/certificate"
 | 
				
			||||||
	"npm/internal/entity/nginxtemplate"
 | 
						"npm/internal/entity/nginxtemplate"
 | 
				
			||||||
	"npm/internal/entity/upstream"
 | 
						"npm/internal/entity/upstream"
 | 
				
			||||||
@@ -17,8 +16,6 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	tableName = "host"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ProxyHostType is self explanatory
 | 
						// ProxyHostType is self explanatory
 | 
				
			||||||
	ProxyHostType = "proxy"
 | 
						ProxyHostType = "proxy"
 | 
				
			||||||
	// RedirectionHostType is self explanatory
 | 
						// RedirectionHostType is self explanatory
 | 
				
			||||||
@@ -27,67 +24,53 @@ const (
 | 
				
			|||||||
	DeadHostType = "dead"
 | 
						DeadHostType = "dead"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Model is the user model
 | 
					// Model is the model
 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID                    int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn             types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						UserID                uint        `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	ModifiedOn            types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Type                  string      `json:"type" gorm:"column:type" filter:"type,string"`
 | 
				
			||||||
	UserID                int          `json:"user_id" db:"user_id" filter:"user_id,integer"`
 | 
						NginxTemplateID       uint        `json:"nginx_template_id" gorm:"column:nginx_template_id" filter:"nginx_template_id,integer"`
 | 
				
			||||||
	Type                  string       `json:"type" db:"type" filter:"type,string"`
 | 
						ListenInterface       string      `json:"listen_interface" gorm:"column:listen_interface" filter:"listen_interface,string"`
 | 
				
			||||||
	NginxTemplateID       int          `json:"nginx_template_id" db:"nginx_template_id" filter:"nginx_template_id,integer"`
 | 
						DomainNames           types.JSONB `json:"domain_names" gorm:"column:domain_names" filter:"domain_names,string"`
 | 
				
			||||||
	ListenInterface       string       `json:"listen_interface" db:"listen_interface" filter:"listen_interface,string"`
 | 
						UpstreamID            uint        `json:"upstream_id" gorm:"column:upstream_id" filter:"upstream_id,integer"`
 | 
				
			||||||
	DomainNames           types.JSONB  `json:"domain_names" db:"domain_names" filter:"domain_names,string"`
 | 
						ProxyScheme           string      `json:"proxy_scheme" gorm:"column:proxy_scheme" filter:"proxy_scheme,string"`
 | 
				
			||||||
	UpstreamID            int          `json:"upstream_id" db:"upstream_id" filter:"upstream_id,integer"`
 | 
						ProxyHost             string      `json:"proxy_host" gorm:"column:proxy_host" filter:"proxy_host,string"`
 | 
				
			||||||
	ProxyScheme           string       `json:"proxy_scheme" db:"proxy_scheme" filter:"proxy_scheme,string"`
 | 
						ProxyPort             int         `json:"proxy_port" gorm:"column:proxy_port" filter:"proxy_port,integer"`
 | 
				
			||||||
	ProxyHost             string       `json:"proxy_host" db:"proxy_host" filter:"proxy_host,string"`
 | 
						CertificateID         uint        `json:"certificate_id" gorm:"column:certificate_id" filter:"certificate_id,integer"`
 | 
				
			||||||
	ProxyPort             int          `json:"proxy_port" db:"proxy_port" filter:"proxy_port,integer"`
 | 
						AccessListID          uint        `json:"access_list_id" gorm:"column:access_list_id" filter:"access_list_id,integer"`
 | 
				
			||||||
	CertificateID         int          `json:"certificate_id" db:"certificate_id" filter:"certificate_id,integer"`
 | 
						SSLForced             bool        `json:"ssl_forced" gorm:"column:ssl_forced" filter:"ssl_forced,boolean"`
 | 
				
			||||||
	AccessListID          int          `json:"access_list_id" db:"access_list_id" filter:"access_list_id,integer"`
 | 
						CachingEnabled        bool        `json:"caching_enabled" gorm:"column:caching_enabled" filter:"caching_enabled,boolean"`
 | 
				
			||||||
	SSLForced             bool         `json:"ssl_forced" db:"ssl_forced" filter:"ssl_forced,boolean"`
 | 
						BlockExploits         bool        `json:"block_exploits" gorm:"column:block_exploits" filter:"block_exploits,boolean"`
 | 
				
			||||||
	CachingEnabled        bool         `json:"caching_enabled" db:"caching_enabled" filter:"caching_enabled,boolean"`
 | 
						AllowWebsocketUpgrade bool        `json:"allow_websocket_upgrade" gorm:"column:allow_websocket_upgrade" filter:"allow_websocket_upgrade,boolean"`
 | 
				
			||||||
	BlockExploits         bool         `json:"block_exploits" db:"block_exploits" filter:"block_exploits,boolean"`
 | 
						HTTP2Support          bool        `json:"http2_support" gorm:"column:http2_support" filter:"http2_support,boolean"`
 | 
				
			||||||
	AllowWebsocketUpgrade bool         `json:"allow_websocket_upgrade" db:"allow_websocket_upgrade" filter:"allow_websocket_upgrade,boolean"`
 | 
						HSTSEnabled           bool        `json:"hsts_enabled" gorm:"column:hsts_enabled" filter:"hsts_enabled,boolean"`
 | 
				
			||||||
	HTTP2Support          bool         `json:"http2_support" db:"http2_support" filter:"http2_support,boolean"`
 | 
						HSTSSubdomains        bool        `json:"hsts_subdomains" gorm:"column:hsts_subdomains" filter:"hsts_subdomains,boolean"`
 | 
				
			||||||
	HSTSEnabled           bool         `json:"hsts_enabled" db:"hsts_enabled" filter:"hsts_enabled,boolean"`
 | 
						Paths                 string      `json:"paths" gorm:"column:paths" filter:"paths,string"`
 | 
				
			||||||
	HSTSSubdomains        bool         `json:"hsts_subdomains" db:"hsts_subdomains" filter:"hsts_subdomains,boolean"`
 | 
						AdvancedConfig        string      `json:"advanced_config" gorm:"column:advanced_config" filter:"advanced_config,string"`
 | 
				
			||||||
	Paths                 string       `json:"paths" db:"paths" filter:"paths,string"`
 | 
						Status                string      `json:"status" gorm:"column:status" filter:"status,string"`
 | 
				
			||||||
	AdvancedConfig        string       `json:"advanced_config" db:"advanced_config" filter:"advanced_config,string"`
 | 
						ErrorMessage          string      `json:"error_message" gorm:"column:error_message" filter:"error_message,string"`
 | 
				
			||||||
	Status                string       `json:"status" db:"status" filter:"status,string"`
 | 
						IsDisabled            bool        `json:"is_disabled" gorm:"column:is_disabled" filter:"is_disabled,boolean"`
 | 
				
			||||||
	ErrorMessage          string       `json:"error_message" db:"error_message" filter:"error_message,string"`
 | 
					 | 
				
			||||||
	IsDisabled            bool         `json:"is_disabled" db:"is_disabled" filter:"is_disabled,boolean"`
 | 
					 | 
				
			||||||
	IsDeleted             bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
	// Expansions
 | 
						// Expansions
 | 
				
			||||||
	Certificate   *certificate.Model   `json:"certificate,omitempty"`
 | 
						Certificate   *certificate.Model   `json:"certificate,omitempty" gorm:"-"`
 | 
				
			||||||
	NginxTemplate *nginxtemplate.Model `json:"nginx_template,omitempty"`
 | 
						NginxTemplate *nginxtemplate.Model `json:"nginx_template,omitempty" gorm:"-"`
 | 
				
			||||||
	User          *user.Model          `json:"user,omitempty"`
 | 
						User          *user.Model          `json:"user,omitempty" gorm:"-"`
 | 
				
			||||||
	Upstream      *upstream.Model      `json:"upstream,omitempty"`
 | 
						Upstream      *upstream.Model      `json:"upstream,omitempty" gorm:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "host"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save(skipConfiguration bool) error {
 | 
					func (m *Model) Save(skipConfiguration bool) error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.UserID == 0 {
 | 
						if m.UserID == 0 {
 | 
				
			||||||
		return eris.Errorf("User ID must be specified")
 | 
							return eris.Errorf("User ID must be specified")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -97,23 +80,20 @@ func (m *Model) Save(skipConfiguration bool) error {
 | 
				
			|||||||
		m.Status = status.StatusReady
 | 
							m.Status = status.StatusReady
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = create(m)
 | 
						result := db.Save(m)
 | 
				
			||||||
	} else {
 | 
						return result.Error
 | 
				
			||||||
		err = update(m)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a host as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(false); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Expand will fill in more properties
 | 
					// Expand will fill in more properties
 | 
				
			||||||
@@ -160,8 +140,8 @@ func (m *Model) GetTemplate() Template {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	t := Template{
 | 
						t := Template{
 | 
				
			||||||
		ID:                    m.ID,
 | 
							ID:                    m.ID,
 | 
				
			||||||
		CreatedOn:             m.CreatedOn.Time.String(),
 | 
							CreatedAt:             fmt.Sprintf("%d", m.CreatedAt), // todo: format as nice string
 | 
				
			||||||
		ModifiedOn:            m.ModifiedOn.Time.String(),
 | 
							UpdatedAt:             fmt.Sprintf("%d", m.UpdatedAt), // todo: format as nice string
 | 
				
			||||||
		UserID:                m.UserID,
 | 
							UserID:                m.UserID,
 | 
				
			||||||
		Type:                  m.Type,
 | 
							Type:                  m.Type,
 | 
				
			||||||
		NginxTemplateID:       m.NginxTemplateID,
 | 
							NginxTemplateID:       m.NginxTemplateID,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package host
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for this list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -4,20 +4,20 @@ import "npm/internal/entity/upstream"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Template is the model given to the template parser, converted from the Model
 | 
					// Template is the model given to the template parser, converted from the Model
 | 
				
			||||||
type Template struct {
 | 
					type Template struct {
 | 
				
			||||||
	ID                    int
 | 
						ID                    uint
 | 
				
			||||||
	CreatedOn             string
 | 
						CreatedAt             string
 | 
				
			||||||
	ModifiedOn            string
 | 
						UpdatedAt             string
 | 
				
			||||||
	UserID                int
 | 
						UserID                uint
 | 
				
			||||||
	Type                  string
 | 
						Type                  string
 | 
				
			||||||
	NginxTemplateID       int
 | 
						NginxTemplateID       uint
 | 
				
			||||||
	ProxyScheme           string
 | 
						ProxyScheme           string
 | 
				
			||||||
	ProxyHost             string
 | 
						ProxyHost             string
 | 
				
			||||||
	ProxyPort             int
 | 
						ProxyPort             int
 | 
				
			||||||
	ListenInterface       string
 | 
						ListenInterface       string
 | 
				
			||||||
	DomainNames           []string
 | 
						DomainNames           []string
 | 
				
			||||||
	UpstreamID            int
 | 
						UpstreamID            uint
 | 
				
			||||||
	CertificateID         int
 | 
						CertificateID         uint
 | 
				
			||||||
	AccessListID          int
 | 
						AccessListID          uint
 | 
				
			||||||
	SSLForced             bool
 | 
						SSLForced             bool
 | 
				
			||||||
	CachingEnabled        bool
 | 
						CachingEnabled        bool
 | 
				
			||||||
	BlockExploits         bool
 | 
						BlockExploits         bool
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										36
									
								
								backend/internal/entity/lists.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								backend/internal/entity/lists.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListableModel is a interface for common use
 | 
				
			||||||
 | 
					type ListableModel interface {
 | 
				
			||||||
 | 
						TableName() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListResponse is the JSON response for users list
 | 
				
			||||||
 | 
					type ListResponse struct {
 | 
				
			||||||
 | 
						Total  int64          `json:"total"`
 | 
				
			||||||
 | 
						Offset int            `json:"offset"`
 | 
				
			||||||
 | 
						Limit  int            `json:"limit"`
 | 
				
			||||||
 | 
						Sort   []model.Sort   `json:"sort"`
 | 
				
			||||||
 | 
						Filter []model.Filter `json:"filter,omitempty"`
 | 
				
			||||||
 | 
						Items  interface{}    `json:"items,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListQueryBuilder is used to setup queries for lists
 | 
				
			||||||
 | 
					func ListQueryBuilder(
 | 
				
			||||||
 | 
						pageInfo *model.PageInfo,
 | 
				
			||||||
 | 
						defaultSort model.Sort,
 | 
				
			||||||
 | 
						filters []model.Filter,
 | 
				
			||||||
 | 
					) *gorm.DB {
 | 
				
			||||||
 | 
						scopes := make([]func(*gorm.DB) *gorm.DB, 0)
 | 
				
			||||||
 | 
						scopes = append(scopes, ScopeOrderBy(pageInfo, defaultSort))
 | 
				
			||||||
 | 
						scopes = append(scopes, ScopeOffsetLimit(pageInfo))
 | 
				
			||||||
 | 
						// scopes = append(scopes, ScopeFilters(GetFilterMap(m)))
 | 
				
			||||||
 | 
						return database.GetDB().Scopes(scopes...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,80 +0,0 @@
 | 
				
			|||||||
package entity
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListQueryBuilder should be able to return the query and params to get items agnostically based
 | 
					 | 
				
			||||||
// on given params.
 | 
					 | 
				
			||||||
func ListQueryBuilder(modelExample interface{}, tableName string, pageInfo *model.PageInfo, defaultSort model.Sort, filters []model.Filter, filterMapFunctions map[string]FilterMapFunction, returnCount bool) (string, []interface{}) {
 | 
					 | 
				
			||||||
	var queryStrings []string
 | 
					 | 
				
			||||||
	var whereStrings []string
 | 
					 | 
				
			||||||
	var params []interface{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if returnCount {
 | 
					 | 
				
			||||||
		queryStrings = append(queryStrings, "SELECT COUNT(*)")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		queryStrings = append(queryStrings, "SELECT *")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	queryStrings = append(queryStrings, fmt.Sprintf("FROM `%s`", tableName))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Append filters to where clause:
 | 
					 | 
				
			||||||
	if filters != nil {
 | 
					 | 
				
			||||||
		filterMap := GetFilterMap(modelExample)
 | 
					 | 
				
			||||||
		filterQuery, filterParams := GenerateSQLFromFilters(filters, filterMap, filterMapFunctions)
 | 
					 | 
				
			||||||
		whereStrings = []string{filterQuery}
 | 
					 | 
				
			||||||
		params = append(params, filterParams...)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Add is deletee check if model has the field
 | 
					 | 
				
			||||||
	if hasDeletedField(modelExample) {
 | 
					 | 
				
			||||||
		params = append(params, 0)
 | 
					 | 
				
			||||||
		whereStrings = append(whereStrings, "`is_deleted` = ?")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Append where clauses to query
 | 
					 | 
				
			||||||
	if len(whereStrings) > 0 {
 | 
					 | 
				
			||||||
		// nolint: gosec
 | 
					 | 
				
			||||||
		queryStrings = append(queryStrings, fmt.Sprintf("WHERE %s", strings.Join(whereStrings, " AND ")))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !returnCount {
 | 
					 | 
				
			||||||
		var orderBy string
 | 
					 | 
				
			||||||
		columns := GetDBColumns(modelExample)
 | 
					 | 
				
			||||||
		orderBy, pageInfo.Sort = database.BuildOrderBySQL(columns, &pageInfo.Sort)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if orderBy != "" {
 | 
					 | 
				
			||||||
			queryStrings = append(queryStrings, orderBy)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			pageInfo.Sort = append(pageInfo.Sort, defaultSort)
 | 
					 | 
				
			||||||
			queryStrings = append(queryStrings, fmt.Sprintf("ORDER BY `%v` COLLATE NOCASE %v", defaultSort.Field, defaultSort.Direction))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		params = append(params, pageInfo.Offset)
 | 
					 | 
				
			||||||
		params = append(params, pageInfo.Limit)
 | 
					 | 
				
			||||||
		queryStrings = append(queryStrings, "LIMIT ?, ?")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return strings.Join(queryStrings, " "), params
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func hasDeletedField(modelExample interface{}) bool {
 | 
					 | 
				
			||||||
	t := reflect.TypeOf(modelExample)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i := 0; i < t.NumField(); i++ {
 | 
					 | 
				
			||||||
		field := t.Field(i)
 | 
					 | 
				
			||||||
		dbTag := field.Tag.Get(DBTagName)
 | 
					 | 
				
			||||||
		if dbTag == "is_deleted" {
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										13
									
								
								backend/internal/entity/model_base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								backend/internal/entity/model_base.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"gorm.io/plugin/soft_delete"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ModelBase include common fields for db control
 | 
				
			||||||
 | 
					type ModelBase struct {
 | 
				
			||||||
 | 
						ID        uint                  `json:"id" gorm:"column:id;primaryKey"`
 | 
				
			||||||
 | 
						CreatedAt int64                 `json:"created_at" gorm:"<-:create;autoCreateTime:milli;column:created_at"`
 | 
				
			||||||
 | 
						UpdatedAt int64                 `json:"updated_at" gorm:"<-;autoUpdateTime:milli;column:updated_at"`
 | 
				
			||||||
 | 
						DeletedAt soft_delete.DeletedAt `json:"-" gorm:"column:is_deleted;softDelete:flag"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package nginxtemplate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,123 +1,41 @@
 | 
				
			|||||||
package nginxtemplate
 | 
					package nginxtemplate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a Host by ID
 | 
					// GetByID finds a Host by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will create a Host from this model
 | 
					 | 
				
			||||||
func Create(host *Model) (int, error) {
 | 
					 | 
				
			||||||
	if host.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create host template when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	host.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		user_id,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		host_type,
 | 
					 | 
				
			||||||
		template,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:user_id,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:host_type,
 | 
					 | 
				
			||||||
		:template,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a Host from this model
 | 
					 | 
				
			||||||
func Update(host *Model) error {
 | 
					 | 
				
			||||||
	if host.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update host template when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	host.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		user_id = :user_id,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		host_type = :host_type,
 | 
					 | 
				
			||||||
		template = :template,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of hosts
 | 
					// List will return a list of hosts
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "created_on",
 | 
							Field:     "created_on",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,75 +1,51 @@
 | 
				
			|||||||
package nginxtemplate
 | 
					package nginxtemplate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/types"
 | 
						"npm/internal/entity"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "nginx_template"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the user model
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID         int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn  types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						UserID   int    `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	ModifiedOn types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Name     string `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	UserID     int          `json:"user_id" db:"user_id" filter:"user_id,integer"`
 | 
						Type     string `json:"type" gorm:"column:type" filter:"type,string"`
 | 
				
			||||||
	Name       string       `json:"name" db:"name" filter:"name,string"`
 | 
						Template string `json:"template" gorm:"column:template" filter:"template,string"`
 | 
				
			||||||
	Type       string       `json:"type" db:"type" filter:"type,string"`
 | 
					 | 
				
			||||||
	Template   string       `json:"template" db:"template" filter:"template,string"`
 | 
					 | 
				
			||||||
	IsDeleted  bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "nginx_template"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.UserID == 0 {
 | 
						if m.UserID == 0 {
 | 
				
			||||||
		return eris.Errorf("User ID must be specified")
 | 
							return eris.Errorf("User ID must be specified")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						result := db.Save(m)
 | 
				
			||||||
	} else {
 | 
						return result.Error
 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a template as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package nginxtemplate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for this list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										62
									
								
								backend/internal/entity/scopes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								backend/internal/entity/scopes.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gorm.io/gorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ScopeOffsetLimit(pageInfo *model.PageInfo) func(db *gorm.DB) *gorm.DB {
 | 
				
			||||||
 | 
						return func(db *gorm.DB) *gorm.DB {
 | 
				
			||||||
 | 
							if pageInfo.Offset > 0 || pageInfo.Limit > 0 {
 | 
				
			||||||
 | 
								return db.Limit(pageInfo.Limit).Offset(pageInfo.Offset)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return db
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ScopeOrderBy(pageInfo *model.PageInfo, defaultSort model.Sort) func(db *gorm.DB) *gorm.DB {
 | 
				
			||||||
 | 
						return func(db *gorm.DB) *gorm.DB {
 | 
				
			||||||
 | 
							if pageInfo.Sort != nil {
 | 
				
			||||||
 | 
								// Sort by items in slice
 | 
				
			||||||
 | 
								return db.Order(sortToOrderString(pageInfo.Sort))
 | 
				
			||||||
 | 
							} else if defaultSort.Field != "" {
 | 
				
			||||||
 | 
								// Default to this sort
 | 
				
			||||||
 | 
								str := defaultSort.Field
 | 
				
			||||||
 | 
								if defaultSort.Direction != "" {
 | 
				
			||||||
 | 
									str = str + " " + defaultSort.Direction
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return db.Order(str)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return db
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ScopeFilters(filters map[string]string) func(db *gorm.DB) *gorm.DB {
 | 
				
			||||||
 | 
						return func(db *gorm.DB) *gorm.DB {
 | 
				
			||||||
 | 
							// todo
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
								if filters != nil {
 | 
				
			||||||
 | 
									filterMap := GetFilterMap(m)
 | 
				
			||||||
 | 
									filterQuery, filterParams := GenerateSQLFromFilters(filters, filterMap, filterMapFunctions)
 | 
				
			||||||
 | 
									whereStrings = []string{filterQuery}
 | 
				
			||||||
 | 
									params = append(params, filterParams...)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							*/
 | 
				
			||||||
 | 
							return db
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func sortToOrderString(sorts []model.Sort) string {
 | 
				
			||||||
 | 
						strs := make([]string, 0)
 | 
				
			||||||
 | 
						for _, i := range sorts {
 | 
				
			||||||
 | 
							str := i.Field
 | 
				
			||||||
 | 
							if i.Direction != "" {
 | 
				
			||||||
 | 
								str = str + " " + i.Direction
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							strs = append(strs, str)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.Join(strs, ", ")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,19 +0,0 @@
 | 
				
			|||||||
package setting
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/config"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ApplySettings will load settings from the DB and apply them where required
 | 
					 | 
				
			||||||
func ApplySettings() {
 | 
					 | 
				
			||||||
	logger.Debug("Applying Settings")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Error-reporting
 | 
					 | 
				
			||||||
	m, err := GetByName("error-reporting")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("ApplySettingsError", err)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		config.ErrorReporting = m.Value.Decoded.(bool)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package setting
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,16 +1,10 @@
 | 
				
			|||||||
package setting
 | 
					package setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
						"npm/internal/config"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a setting by ID
 | 
					// GetByID finds a setting by ID
 | 
				
			||||||
@@ -27,95 +21,30 @@ func GetByName(name string) (Model, error) {
 | 
				
			|||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will Create a Setting from this model
 | 
					 | 
				
			||||||
func Create(setting *Model) (int, error) {
 | 
					 | 
				
			||||||
	if setting.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create setting when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setting.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		value
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:value
 | 
					 | 
				
			||||||
	)`, setting)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a Setting from this model
 | 
					 | 
				
			||||||
func Update(setting *Model) error {
 | 
					 | 
				
			||||||
	if setting.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update setting when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setting.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		value = :value
 | 
					 | 
				
			||||||
	WHERE id = :id`, setting)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of settings
 | 
					// List will return a list of settings
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%+v", queryErr)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%+v", err)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
@@ -126,3 +55,16 @@ func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return result, nil
 | 
						return result, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ApplySettings will load settings from the DB and apply them where required
 | 
				
			||||||
 | 
					func ApplySettings() {
 | 
				
			||||||
 | 
						logger.Debug("Applying Settings")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Error-reporting
 | 
				
			||||||
 | 
						m, err := GetByName("error-reporting")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Error("ApplySettingsError", err)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							config.ErrorReporting = m.Value.String() == "true"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,73 +1,50 @@
 | 
				
			|||||||
package setting
 | 
					package setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/types"
 | 
						"npm/internal/entity"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gorm.io/datatypes"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "setting"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the user model
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID          int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn   types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						Name        string         `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	ModifiedOn  types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Description string         `json:"description" gorm:"column:description" filter:"description,string"`
 | 
				
			||||||
	Name        string       `json:"name" db:"name" filter:"name,string"`
 | 
						Value       datatypes.JSON `json:"value" gorm:"column:value"`
 | 
				
			||||||
	Description string       `json:"description" db:"description" filter:"description,string"`
 | 
					 | 
				
			||||||
	Value       types.JSONB  `json:"value" db:"value"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "setting"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id int) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE `id` = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByName will load from a Name
 | 
					// LoadByName will load from a Name
 | 
				
			||||||
func (m *Model) LoadByName(name string) error {
 | 
					func (m *Model) LoadByName(name string) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE LOWER(`name`) = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{strings.TrimSpace(strings.ToLower(name))}
 | 
						result := db.Where("name = ?", strings.ToLower(name)).First(&m)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ensure name is trimmed of whitespace
 | 
						// ensure name is trimmed of whitespace
 | 
				
			||||||
	m.Name = strings.TrimSpace(m.Name)
 | 
						m.Name = strings.TrimSpace(m.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						if result := db.Save(m); result.Error != nil {
 | 
				
			||||||
	} else {
 | 
							return result.Error
 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Reapply settings
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
	ApplySettings()
 | 
						ApplySettings()
 | 
				
			||||||
	}
 | 
						return nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package setting
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for settings list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package stream
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,129 +1,41 @@
 | 
				
			|||||||
package stream
 | 
					package stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a auth by ID
 | 
					// GetByID finds a auth by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will create a Auth from this model
 | 
					 | 
				
			||||||
func Create(host *Model) (int, error) {
 | 
					 | 
				
			||||||
	if host.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create stream when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	host.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		user_id,
 | 
					 | 
				
			||||||
		provider,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		domain_names,
 | 
					 | 
				
			||||||
		expires_on,
 | 
					 | 
				
			||||||
		meta,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:user_id,
 | 
					 | 
				
			||||||
		:provider,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:domain_names,
 | 
					 | 
				
			||||||
		:expires_on,
 | 
					 | 
				
			||||||
		:meta,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a Host from this model
 | 
					 | 
				
			||||||
func Update(host *Model) error {
 | 
					 | 
				
			||||||
	if host.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update stream when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	host.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		user_id = :user_id,
 | 
					 | 
				
			||||||
		provider = :provider,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		domain_names = :domain_names,
 | 
					 | 
				
			||||||
		expires_on = :expires_on,
 | 
					 | 
				
			||||||
		meta = :meta,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of hosts
 | 
					// List will return a list of hosts
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,77 +1,54 @@
 | 
				
			|||||||
package stream
 | 
					package stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/types"
 | 
						"npm/internal/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "stream"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the user model
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID          int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn   types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						ExpiresOn   types.DBDate `json:"expires_on" gorm:"column:expires_on" filter:"expires_on,integer"`
 | 
				
			||||||
	ModifiedOn  types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						UserID      int          `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	ExpiresOn   types.DBDate `json:"expires_on" db:"expires_on" filter:"expires_on,integer"`
 | 
						Provider    string       `json:"provider" gorm:"column:provider" filter:"provider,string"`
 | 
				
			||||||
	UserID      int          `json:"user_id" db:"user_id" filter:"user_id,integer"`
 | 
						Name        string       `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	Provider    string       `json:"provider" db:"provider" filter:"provider,string"`
 | 
						DomainNames types.JSONB  `json:"domain_names" gorm:"column:domain_names" filter:"domain_names,string"`
 | 
				
			||||||
	Name        string       `json:"name" db:"name" filter:"name,string"`
 | 
						Meta        types.JSONB  `json:"-" gorm:"column:meta"`
 | 
				
			||||||
	DomainNames types.JSONB  `json:"domain_names" db:"domain_names" filter:"domain_names,string"`
 | 
					 | 
				
			||||||
	Meta        types.JSONB  `json:"-" db:"meta"`
 | 
					 | 
				
			||||||
	IsDeleted   bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "stream"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.UserID == 0 {
 | 
						if m.UserID == 0 {
 | 
				
			||||||
		return eris.Errorf("User ID must be specified")
 | 
							return eris.Errorf("User ID must be specified")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						result := db.Save(m)
 | 
				
			||||||
	} else {
 | 
						return result.Error
 | 
				
			||||||
		err = Update(m)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a host as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package stream
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for this list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package upstream
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,147 +1,38 @@
 | 
				
			|||||||
package upstream
 | 
					package upstream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a Upstream by ID
 | 
					// GetByID finds a Upstream by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// create will create a Upstream from this model
 | 
					 | 
				
			||||||
func create(u *Model) (int, error) {
 | 
					 | 
				
			||||||
	if u.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create upstream when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	u.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		user_id,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		nginx_template_id,
 | 
					 | 
				
			||||||
		ip_hash,
 | 
					 | 
				
			||||||
		ntlm,
 | 
					 | 
				
			||||||
		keepalive,
 | 
					 | 
				
			||||||
		keepalive_requests,
 | 
					 | 
				
			||||||
		keepalive_time,
 | 
					 | 
				
			||||||
		keepalive_timeout,
 | 
					 | 
				
			||||||
		advanced_config,
 | 
					 | 
				
			||||||
		status,
 | 
					 | 
				
			||||||
		error_message,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:user_id,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:nginx_template_id,
 | 
					 | 
				
			||||||
		:ip_hash,
 | 
					 | 
				
			||||||
		:ntlm,
 | 
					 | 
				
			||||||
		:keepalive,
 | 
					 | 
				
			||||||
		:keepalive_requests,
 | 
					 | 
				
			||||||
		:keepalive_time,
 | 
					 | 
				
			||||||
		:keepalive_timeout,
 | 
					 | 
				
			||||||
		:advanced_config,
 | 
					 | 
				
			||||||
		:status,
 | 
					 | 
				
			||||||
		:error_message,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logger.Debug("Created Upstream: %+v", u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// update will Update a Upstream from this model
 | 
					 | 
				
			||||||
func update(u *Model) error {
 | 
					 | 
				
			||||||
	if u.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update upstream when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	u.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		user_id = :user_id,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		nginx_template_id = :nginx_template_id,
 | 
					 | 
				
			||||||
		ip_hash = :ip_hash,
 | 
					 | 
				
			||||||
		ntlm = :ntlm,
 | 
					 | 
				
			||||||
		keepalive = :keepalive,
 | 
					 | 
				
			||||||
		keepalive_requests = :keepalive_requests,
 | 
					 | 
				
			||||||
		keepalive_time = :keepalive_time,
 | 
					 | 
				
			||||||
		advanced_config = :advanced_config,
 | 
					 | 
				
			||||||
		status = :status,
 | 
					 | 
				
			||||||
		error_message = :error_message,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logger.Debug("Updated Upstream: %+v", u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// List will return a list of Upstreams
 | 
					// List will return a list of Upstreams
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Expand to get servers, at a minimum
 | 
						// Expand to get servers, at a minimum
 | 
				
			||||||
@@ -150,7 +41,7 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (Lis
 | 
				
			|||||||
		items[idx].Expand(expand)
 | 
							items[idx].Expand(expand)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,79 +1,55 @@
 | 
				
			|||||||
package upstream
 | 
					package upstream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/entity/nginxtemplate"
 | 
						"npm/internal/entity/nginxtemplate"
 | 
				
			||||||
	"npm/internal/entity/upstreamserver"
 | 
						"npm/internal/entity/upstreamserver"
 | 
				
			||||||
	"npm/internal/entity/user"
 | 
						"npm/internal/entity/user"
 | 
				
			||||||
	"npm/internal/status"
 | 
						"npm/internal/status"
 | 
				
			||||||
	"npm/internal/types"
 | 
					 | 
				
			||||||
	"npm/internal/util"
 | 
						"npm/internal/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "upstream"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the Upstream model
 | 
					 | 
				
			||||||
// See: http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream
 | 
					// See: http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream
 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID                int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn         types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						UserID            uint   `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	ModifiedOn        types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Name              string `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	UserID            int          `json:"user_id" db:"user_id" filter:"user_id,integer"`
 | 
						NginxTemplateID   uint   `json:"nginx_template_id" gorm:"column:nginx_template_id" filter:"nginx_template_id,integer"`
 | 
				
			||||||
	Name              string       `json:"name" db:"name" filter:"name,string"`
 | 
						IPHash            bool   `json:"ip_hash" gorm:"column:ip_hash" filter:"ip_hash,boolean"`
 | 
				
			||||||
	NginxTemplateID   int          `json:"nginx_template_id" db:"nginx_template_id" filter:"nginx_template_id,integer"`
 | 
						NTLM              bool   `json:"ntlm" gorm:"column:ntlm" filter:"ntlm,boolean"`
 | 
				
			||||||
	IPHash            bool         `json:"ip_hash" db:"ip_hash" filter:"ip_hash,boolean"`
 | 
						Keepalive         int    `json:"keepalive" gorm:"column:keepalive" filter:"keepalive,integer"`
 | 
				
			||||||
	NTLM              bool         `json:"ntlm" db:"ntlm" filter:"ntlm,boolean"`
 | 
						KeepaliveRequests int    `json:"keepalive_requests" gorm:"column:keepalive_requests" filter:"keepalive_requests,integer"`
 | 
				
			||||||
	Keepalive         int          `json:"keepalive" db:"keepalive" filter:"keepalive,integer"`
 | 
						KeepaliveTime     string `json:"keepalive_time" gorm:"column:keepalive_time" filter:"keepalive_time,string"`
 | 
				
			||||||
	KeepaliveRequests int          `json:"keepalive_requests" db:"keepalive_requests" filter:"keepalive_requests,integer"`
 | 
						KeepaliveTimeout  string `json:"keepalive_timeout" gorm:"column:keepalive_timeout" filter:"keepalive_timeout,string"`
 | 
				
			||||||
	KeepaliveTime     string       `json:"keepalive_time" db:"keepalive_time" filter:"keepalive_time,string"`
 | 
						AdvancedConfig    string `json:"advanced_config" gorm:"column:advanced_config" filter:"advanced_config,string"`
 | 
				
			||||||
	KeepaliveTimeout  string       `json:"keepalive_timeout" db:"keepalive_timeout" filter:"keepalive_timeout,string"`
 | 
						Status            string `json:"status" gorm:"column:status" filter:"status,string"`
 | 
				
			||||||
	AdvancedConfig    string       `json:"advanced_config" db:"advanced_config" filter:"advanced_config,string"`
 | 
						ErrorMessage      string `json:"error_message" gorm:"column:error_message" filter:"error_message,string"`
 | 
				
			||||||
	Status            string       `json:"status" db:"status" filter:"status,string"`
 | 
					 | 
				
			||||||
	ErrorMessage      string       `json:"error_message" db:"error_message" filter:"error_message,string"`
 | 
					 | 
				
			||||||
	IsDeleted         bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
	// Expansions
 | 
						// Expansions
 | 
				
			||||||
	Servers       []upstreamserver.Model `json:"servers"`
 | 
						Servers       []upstreamserver.Model `json:"servers" gorm:"-"`
 | 
				
			||||||
	NginxTemplate *nginxtemplate.Model   `json:"nginx_template,omitempty"`
 | 
						NginxTemplate *nginxtemplate.Model   `json:"nginx_template,omitempty" gorm:"-"`
 | 
				
			||||||
	User          *user.Model            `json:"user,omitempty"`
 | 
						User          *user.Model            `json:"user,omitempty" gorm:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "upstream"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	err := m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		err = m.Expand(nil)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save(skipConfiguration bool) error {
 | 
					func (m *Model) Save(skipConfiguration bool) error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.UserID == 0 {
 | 
						if m.UserID == 0 {
 | 
				
			||||||
		return eris.Errorf("User ID must be specified")
 | 
							return eris.Errorf("User ID must be specified")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -86,14 +62,13 @@ func (m *Model) Save(skipConfiguration bool) error {
 | 
				
			|||||||
		m.Status = status.StatusReady
 | 
							m.Status = status.StatusReady
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = create(m)
 | 
						if result := db.Save(m); result.Error != nil {
 | 
				
			||||||
	} else {
 | 
							return result.Error
 | 
				
			||||||
		err = update(m)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Save Servers
 | 
						// Save Servers
 | 
				
			||||||
	if err == nil {
 | 
						var err error
 | 
				
			||||||
	for idx := range m.Servers {
 | 
						for idx := range m.Servers {
 | 
				
			||||||
		// Continue if previous iteration didn't cause an error
 | 
							// Continue if previous iteration didn't cause an error
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
@@ -101,19 +76,19 @@ func (m *Model) Save(skipConfiguration bool) error {
 | 
				
			|||||||
			err = m.Servers[idx].Save()
 | 
								err = m.Servers[idx].Save()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete will mark a upstream as deleted
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(false); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Expand will fill in more properties
 | 
					// Expand will fill in more properties
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package upstream
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for this list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package upstreamserver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,16 +1,9 @@
 | 
				
			|||||||
package upstreamserver
 | 
					package upstreamserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a Upstream Server by ID
 | 
					// GetByID finds a Upstream Server by ID
 | 
				
			||||||
@@ -21,128 +14,37 @@ func GetByID(id int) (Model, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByUpstreamID finds all servers in the upstream
 | 
					// GetByUpstreamID finds all servers in the upstream
 | 
				
			||||||
func GetByUpstreamID(upstreamID int) ([]Model, error) {
 | 
					func GetByUpstreamID(upstreamID uint) ([]Model, error) {
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query := `SELECT * FROM ` + fmt.Sprintf("`%s`", tableName) + ` WHERE upstream_id = ? ORDER BY server`
 | 
						db := database.GetDB()
 | 
				
			||||||
	db := database.GetInstance()
 | 
						result := db.Where("upstream_id = ?", upstreamID).Order("server ASC").Find(&items)
 | 
				
			||||||
	err := db.Select(&items, query, upstreamID)
 | 
						return items, result.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %d", query, upstreamID)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return items, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// create will create a Upstream Server from this model
 | 
					 | 
				
			||||||
func create(u *Model) (int, error) {
 | 
					 | 
				
			||||||
	if u.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create upstream server when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	u.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		upstream_id,
 | 
					 | 
				
			||||||
		server,
 | 
					 | 
				
			||||||
		weight,
 | 
					 | 
				
			||||||
		max_conns,
 | 
					 | 
				
			||||||
		max_fails,
 | 
					 | 
				
			||||||
		fail_timeout,
 | 
					 | 
				
			||||||
		backup,
 | 
					 | 
				
			||||||
		is_deleted
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:upstream_id,
 | 
					 | 
				
			||||||
		:server,
 | 
					 | 
				
			||||||
		:weight,
 | 
					 | 
				
			||||||
		:max_conns,
 | 
					 | 
				
			||||||
		:max_fails,
 | 
					 | 
				
			||||||
		:fail_timeout,
 | 
					 | 
				
			||||||
		:backup,
 | 
					 | 
				
			||||||
		:is_deleted
 | 
					 | 
				
			||||||
	)`, u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logger.Debug("Created Upstream Server: %+v", u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// update will Update a Upstream from this model
 | 
					 | 
				
			||||||
func update(u *Model) error {
 | 
					 | 
				
			||||||
	if u.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update upstream server when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	u.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		upstream_id = :upstream_id,
 | 
					 | 
				
			||||||
		server = :server,
 | 
					 | 
				
			||||||
		weight = :weight,
 | 
					 | 
				
			||||||
		max_conns = :max_conns,
 | 
					 | 
				
			||||||
		max_fails = :max_fails,
 | 
					 | 
				
			||||||
		fail_timeout = :fail_timeout,
 | 
					 | 
				
			||||||
		backup = :backup,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	logger.Debug("Updated Upstream Server: %+v", u)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List will return a list of Upstreams
 | 
					// List will return a list of Upstreams
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "server",
 | 
							Field:     "server",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("%s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,78 +1,48 @@
 | 
				
			|||||||
package upstreamserver
 | 
					package upstreamserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/types"
 | 
						"npm/internal/entity"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "upstream_server"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the upstream model
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID          int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	CreatedOn   types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						UpstreamID  uint   `json:"upstream_id" gorm:"column:upstream_id" filter:"upstream_id,integer"`
 | 
				
			||||||
	ModifiedOn  types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						Server      string `json:"server" gorm:"column:server" filter:"server,string"`
 | 
				
			||||||
	UpstreamID  int          `json:"upstream_id" db:"upstream_id" filter:"upstream_id,integer"`
 | 
						Weight      int    `json:"weight" gorm:"column:weight" filter:"weight,integer"`
 | 
				
			||||||
	Server      string       `json:"server" db:"server" filter:"server,string"`
 | 
						MaxConns    int    `json:"max_conns" gorm:"column:max_conns" filter:"max_conns,integer"`
 | 
				
			||||||
	Weight      int          `json:"weight" db:"weight" filter:"weight,integer"`
 | 
						MaxFails    int    `json:"max_fails" gorm:"column:max_fails" filter:"max_fails,integer"`
 | 
				
			||||||
	MaxConns    int          `json:"max_conns" db:"max_conns" filter:"max_conns,integer"`
 | 
						FailTimeout int    `json:"fail_timeout" gorm:"column:fail_timeout" filter:"fail_timeout,integer"`
 | 
				
			||||||
	MaxFails    int          `json:"max_fails" db:"max_fails" filter:"max_fails,integer"`
 | 
						Backup      bool   `json:"backup" gorm:"column:is_backup" filter:"backup,boolean"`
 | 
				
			||||||
	FailTimeout int          `json:"fail_timeout" db:"fail_timeout" filter:"fail_timeout,integer"`
 | 
					 | 
				
			||||||
	Backup      bool         `json:"backup" db:"backup" filter:"backup,boolean"`
 | 
					 | 
				
			||||||
	IsDeleted   bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	return database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
 | 
						return "upstream_server"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id int) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, 0}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					 | 
				
			||||||
	var d types.DBDate
 | 
					 | 
				
			||||||
	d.Time = time.Now()
 | 
					 | 
				
			||||||
	if created {
 | 
					 | 
				
			||||||
		m.CreatedOn = d
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	m.ModifiedOn = d
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Save(m)
 | 
				
			||||||
	if m.UpstreamID == 0 {
 | 
						return result.Error
 | 
				
			||||||
		return eris.Errorf("Upstream ID must be specified")
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
					// Delete will mark row as deleted
 | 
				
			||||||
		m.ID, err = create(m)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		err = update(m)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete will mark a upstream as deleted
 | 
					 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package upstreamserver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for this list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
package user
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/entity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getFilterMapFunctions is a map of functions that should be executed
 | 
					 | 
				
			||||||
// during the filtering process, if a field is defined here then the value in
 | 
					 | 
				
			||||||
// the filter will be given to the defined function and it will return a new
 | 
					 | 
				
			||||||
// value for use in the sql query.
 | 
					 | 
				
			||||||
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
 | 
					 | 
				
			||||||
	// if len(filterMapFunctions) == 0 {
 | 
					 | 
				
			||||||
	// TODO: See internal/model/file_item.go:620 for an example
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filterMapFunctions
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetFilterSchema returns filter schema
 | 
					 | 
				
			||||||
func GetFilterSchema() string {
 | 
					 | 
				
			||||||
	var m Model
 | 
					 | 
				
			||||||
	return entity.GetFilterSchema(m)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,20 +1,14 @@
 | 
				
			|||||||
package user
 | 
					package user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/errors"
 | 
					 | 
				
			||||||
	"npm/internal/logger"
 | 
						"npm/internal/logger"
 | 
				
			||||||
	"npm/internal/model"
 | 
						"npm/internal/model"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetByID finds a user by ID
 | 
					// GetByID finds a user by ID
 | 
				
			||||||
func GetByID(id int) (Model, error) {
 | 
					func GetByID(id uint) (Model, error) {
 | 
				
			||||||
	var m Model
 | 
						var m Model
 | 
				
			||||||
	err := m.LoadByID(id)
 | 
						err := m.LoadByID(id)
 | 
				
			||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
@@ -27,140 +21,38 @@ func GetByEmail(email string) (Model, error) {
 | 
				
			|||||||
	return m, err
 | 
						return m, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create will create a User from given model
 | 
					 | 
				
			||||||
func Create(user *Model) (int, error) {
 | 
					 | 
				
			||||||
	// We need to ensure that a user can't be created with the same email
 | 
					 | 
				
			||||||
	// as an existing non-deleted user. Usually you would do this with the
 | 
					 | 
				
			||||||
	// database schema, but it's a bit more complex because of the is_deleted field.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if user.ID != 0 {
 | 
					 | 
				
			||||||
		return 0, eris.New("Cannot create user when model already has an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check if an existing user with this email exists
 | 
					 | 
				
			||||||
	_, err := GetByEmail(user.Email)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		return 0, errors.ErrDuplicateEmailUser
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	user.Touch(true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
 | 
					 | 
				
			||||||
		created_on,
 | 
					 | 
				
			||||||
		modified_on,
 | 
					 | 
				
			||||||
		name,
 | 
					 | 
				
			||||||
		nickname,
 | 
					 | 
				
			||||||
		email,
 | 
					 | 
				
			||||||
		is_disabled
 | 
					 | 
				
			||||||
	) VALUES (
 | 
					 | 
				
			||||||
		:created_on,
 | 
					 | 
				
			||||||
		:modified_on,
 | 
					 | 
				
			||||||
		:name,
 | 
					 | 
				
			||||||
		:nickname,
 | 
					 | 
				
			||||||
		:email,
 | 
					 | 
				
			||||||
		:is_disabled
 | 
					 | 
				
			||||||
	)`, user)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	last, lastErr := result.LastInsertId()
 | 
					 | 
				
			||||||
	if lastErr != nil {
 | 
					 | 
				
			||||||
		return 0, lastErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return int(last), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update will Update a User from this model
 | 
					 | 
				
			||||||
func Update(user *Model) error {
 | 
					 | 
				
			||||||
	if user.ID == 0 {
 | 
					 | 
				
			||||||
		return eris.New("Cannot update user when model doesn't have an ID")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Check that the email address isn't associated with another user
 | 
					 | 
				
			||||||
	if existingUser, _ := GetByEmail(user.Email); existingUser.ID != 0 && existingUser.ID != user.ID {
 | 
					 | 
				
			||||||
		return errors.ErrDuplicateEmailUser
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	user.Touch(false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	// nolint: gosec
 | 
					 | 
				
			||||||
	_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
 | 
					 | 
				
			||||||
		created_on = :created_on,
 | 
					 | 
				
			||||||
		modified_on = :modified_on,
 | 
					 | 
				
			||||||
		name = :name,
 | 
					 | 
				
			||||||
		nickname = :nickname,
 | 
					 | 
				
			||||||
		email = :email,
 | 
					 | 
				
			||||||
		is_disabled = :is_disabled,
 | 
					 | 
				
			||||||
		is_deleted = :is_deleted
 | 
					 | 
				
			||||||
	WHERE id = :id`, user)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsEnabled is used by middleware to ensure the user is still enabled
 | 
					// IsEnabled is used by middleware to ensure the user is still enabled
 | 
				
			||||||
// returns (userExist, isEnabled)
 | 
					// returns (userExist, isEnabled)
 | 
				
			||||||
func IsEnabled(userID int) (bool, bool) {
 | 
					func IsEnabled(userID uint) (bool, bool) {
 | 
				
			||||||
	// nolint: gosec
 | 
						var user Model
 | 
				
			||||||
	query := `SELECT is_disabled FROM ` + fmt.Sprintf("`%s`", tableName) + ` WHERE id = ? AND is_deleted = ?`
 | 
						db := database.GetDB()
 | 
				
			||||||
	disabled := true
 | 
						if result := db.First(&user, userID); result.Error != nil {
 | 
				
			||||||
	db := database.GetInstance()
 | 
					 | 
				
			||||||
	err := db.QueryRowx(query, userID, 0).Scan(&disabled)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err == sql.ErrNoRows {
 | 
					 | 
				
			||||||
		return false, false
 | 
							return false, false
 | 
				
			||||||
	} else if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("QueryError", err)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return true, !user.IsDisabled
 | 
				
			||||||
	return true, !disabled
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List will return a list of users
 | 
					// List will return a list of users
 | 
				
			||||||
func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (ListResponse, error) {
 | 
					func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (entity.ListResponse, error) {
 | 
				
			||||||
	var result ListResponse
 | 
						var result entity.ListResponse
 | 
				
			||||||
	var exampleModel Model
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defaultSort := model.Sort{
 | 
						defaultSort := model.Sort{
 | 
				
			||||||
		Field:     "name",
 | 
							Field:     "name",
 | 
				
			||||||
		Direction: "ASC",
 | 
							Direction: "ASC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						dbo := entity.ListQueryBuilder(&pageInfo, defaultSort, filters)
 | 
				
			||||||
	if db == nil {
 | 
					 | 
				
			||||||
		return result, errors.ErrDatabaseUnavailable
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
		filters = append(filters, model.Filter{
 | 
					 | 
				
			||||||
			Field:    "is_system",
 | 
					 | 
				
			||||||
			Modifier: "equals",
 | 
					 | 
				
			||||||
			Value:    []string{"0"},
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	*/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get count of items in this search
 | 
						// Get count of items in this search
 | 
				
			||||||
	query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
 | 
						var totalRows int64
 | 
				
			||||||
	countRow := db.QueryRowx(query, params...)
 | 
						if res := dbo.Model(&Model{}).Count(&totalRows); res.Error != nil {
 | 
				
			||||||
	var totalRows int
 | 
							return result, res.Error
 | 
				
			||||||
	queryErr := countRow.Scan(&totalRows)
 | 
					 | 
				
			||||||
	if queryErr != nil && queryErr != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("Query: %s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, queryErr
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get rows
 | 
						// Get rows
 | 
				
			||||||
	items := make([]Model, 0)
 | 
						items := make([]Model, 0)
 | 
				
			||||||
	query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
 | 
						if res := dbo.Find(&items); res.Error != nil {
 | 
				
			||||||
	err := db.Select(&items, query, params...)
 | 
							return result, res.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Debug("Query: %s -- %+v", query, params)
 | 
					 | 
				
			||||||
		return result, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for idx := range items {
 | 
						for idx := range items {
 | 
				
			||||||
@@ -176,7 +68,7 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (Lis
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result = ListResponse{
 | 
						result = entity.ListResponse{
 | 
				
			||||||
		Items:  items,
 | 
							Items:  items,
 | 
				
			||||||
		Total:  totalRows,
 | 
							Total:  totalRows,
 | 
				
			||||||
		Limit:  pageInfo.Limit,
 | 
							Limit:  pageInfo.Limit,
 | 
				
			||||||
@@ -190,41 +82,21 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (Lis
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// DeleteAll will do just that, and should only be used for testing purposes.
 | 
					// DeleteAll will do just that, and should only be used for testing purposes.
 | 
				
			||||||
func DeleteAll() error {
 | 
					func DeleteAll() error {
 | 
				
			||||||
	db := database.GetInstance()
 | 
						db := database.GetDB()
 | 
				
			||||||
	_, err := db.Exec(fmt.Sprintf("DELETE FROM `%s`", tableName))
 | 
						result := db.Exec("DELETE FROM users")
 | 
				
			||||||
	return err
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCapabilities gets capabilities for a user
 | 
					// GetCapabilities gets capabilities for a user
 | 
				
			||||||
func GetCapabilities(userID int) ([]string, error) {
 | 
					func GetCapabilities(userID uint) ([]string, error) {
 | 
				
			||||||
	var capabilities []string
 | 
						capabilities := make([]string, 0)
 | 
				
			||||||
	db := database.GetInstance()
 | 
						var hasCapabilities []UserHasCapabilityModel
 | 
				
			||||||
	if db == nil {
 | 
						db := database.GetDB()
 | 
				
			||||||
		return []string{}, errors.ErrDatabaseUnavailable
 | 
						if result := db.Where("user_id = ?", userID).Find(&hasCapabilities); result.Error != nil {
 | 
				
			||||||
 | 
							return nil, result.Error
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						for _, obj := range hasCapabilities {
 | 
				
			||||||
	query := `SELECT c.name FROM "user_has_capability" h
 | 
							capabilities = append(capabilities, obj.CapabilityName)
 | 
				
			||||||
		INNER JOIN "capability" c ON c.id = h.capability_id
 | 
					 | 
				
			||||||
		WHERE h.user_id = ?`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rows, err := db.Query(query, userID)
 | 
					 | 
				
			||||||
	if err != nil && err != sql.ErrNoRows {
 | 
					 | 
				
			||||||
		logger.Debug("QUERY: %v -- %v", query, userID)
 | 
					 | 
				
			||||||
		return []string{}, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// nolint: errcheck
 | 
					 | 
				
			||||||
	defer rows.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for rows.Next() {
 | 
					 | 
				
			||||||
		var name string
 | 
					 | 
				
			||||||
		err := rows.Scan(&name)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return []string{}, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		capabilities = append(capabilities, name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return capabilities, nil
 | 
						return capabilities, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,62 +1,67 @@
 | 
				
			|||||||
package user
 | 
					package user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"npm/internal/database"
 | 
						"npm/internal/database"
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/entity/auth"
 | 
						"npm/internal/entity/auth"
 | 
				
			||||||
	"npm/internal/errors"
 | 
						"npm/internal/errors"
 | 
				
			||||||
	"npm/internal/logger"
 | 
					 | 
				
			||||||
	"npm/internal/types"
 | 
					 | 
				
			||||||
	"npm/internal/util"
 | 
						"npm/internal/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/drexedam/gravatar"
 | 
						"github.com/drexedam/gravatar"
 | 
				
			||||||
	"github.com/rotisserie/eris"
 | 
						"github.com/rotisserie/eris"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					// Model is the model
 | 
				
			||||||
	tableName = "user"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Model is the user model
 | 
					 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	ID          int          `json:"id" db:"id" filter:"id,integer"`
 | 
						entity.ModelBase
 | 
				
			||||||
	Name        string       `json:"name" db:"name" filter:"name,string"`
 | 
						Name       string `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	Nickname    string       `json:"nickname" db:"nickname" filter:"nickname,string"`
 | 
						Nickname   string `json:"nickname" gorm:"column:nickname" filter:"nickname,string"`
 | 
				
			||||||
	Email       string       `json:"email" db:"email" filter:"email,email"`
 | 
						Email      string `json:"email" gorm:"column:email" filter:"email,email"`
 | 
				
			||||||
	CreatedOn   types.DBDate `json:"created_on" db:"created_on" filter:"created_on,integer"`
 | 
						IsDisabled bool   `json:"is_disabled" gorm:"column:is_disabled" filter:"is_disabled,boolean"`
 | 
				
			||||||
	ModifiedOn  types.DBDate `json:"modified_on" db:"modified_on" filter:"modified_on,integer"`
 | 
						IsSystem   bool   `json:"is_system,omitempty" gorm:"column:is_system"`
 | 
				
			||||||
	GravatarURL string       `json:"gravatar_url"`
 | 
						// Other
 | 
				
			||||||
	IsDisabled  bool         `json:"is_disabled" db:"is_disabled" filter:"is_disabled,boolean"`
 | 
						GravatarURL string `json:"gravatar_url" gorm:"-"`
 | 
				
			||||||
	IsSystem    bool         `json:"is_system,omitempty" db:"is_system"`
 | 
					 | 
				
			||||||
	IsDeleted   bool         `json:"is_deleted,omitempty" db:"is_deleted"`
 | 
					 | 
				
			||||||
	// Expansions
 | 
						// Expansions
 | 
				
			||||||
	Auth         *auth.Model `json:"auth,omitempty" db:"-"`
 | 
						Auth         *auth.Model `json:"auth,omitempty" gorm:"-"`
 | 
				
			||||||
	Capabilities []string    `json:"capabilities,omitempty"`
 | 
						Capabilities []string    `json:"capabilities,omitempty" gorm:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Model) getByQuery(query string, params []interface{}) error {
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
	err := database.GetByQuery(m, query, params)
 | 
					func (Model) TableName() string {
 | 
				
			||||||
	m.generateGravatar()
 | 
						return "user"
 | 
				
			||||||
	return err
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UserHasCapabilityModel is the model
 | 
				
			||||||
 | 
					type UserHasCapabilityModel struct {
 | 
				
			||||||
 | 
						UserID         uint   `json:"user_id" gorm:"column:user_id"`
 | 
				
			||||||
 | 
						CapabilityName string `json:"name" gorm:"column:capability_name"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TableName overrides the table name used by gorm
 | 
				
			||||||
 | 
					func (UserHasCapabilityModel) TableName() string {
 | 
				
			||||||
 | 
						return "user_has_capability"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByID will load from an ID
 | 
					// LoadByID will load from an ID
 | 
				
			||||||
func (m *Model) LoadByID(id int) error {
 | 
					func (m *Model) LoadByID(id uint) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ? AND is_deleted = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{id, false}
 | 
						result := db.First(&m, id)
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadByEmail will load from an Email
 | 
					// LoadByEmail will load from an Email
 | 
				
			||||||
func (m *Model) LoadByEmail(email string) error {
 | 
					func (m *Model) LoadByEmail(email string) error {
 | 
				
			||||||
	query := fmt.Sprintf("SELECT * FROM `%s` WHERE email = ? AND is_deleted = ? AND is_system = ? LIMIT 1", tableName)
 | 
						db := database.GetDB()
 | 
				
			||||||
	params := []interface{}{strings.TrimSpace(strings.ToLower(email)), false, false}
 | 
						result := db.
 | 
				
			||||||
	return m.getByQuery(query, params)
 | 
							Where("email = ?", strings.TrimSpace(strings.ToLower(email))).
 | 
				
			||||||
 | 
							Where("is_system = ?", false).
 | 
				
			||||||
 | 
							First(&m)
 | 
				
			||||||
 | 
						return result.Error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
// Touch will update model's timestamp(s)
 | 
					// Touch will update model's timestamp(s)
 | 
				
			||||||
func (m *Model) Touch(created bool) {
 | 
					func (m *Model) Touch(created bool) {
 | 
				
			||||||
	var d types.DBDate
 | 
						var d types.DBDate
 | 
				
			||||||
@@ -67,34 +72,31 @@ func (m *Model) Touch(created bool) {
 | 
				
			|||||||
	m.ModifiedOn = d
 | 
						m.ModifiedOn = d
 | 
				
			||||||
	m.generateGravatar()
 | 
						m.generateGravatar()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save will save this model to the DB
 | 
					// Save will save this model to the DB
 | 
				
			||||||
func (m *Model) Save() error {
 | 
					func (m *Model) Save() error {
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	// Ensure email is nice
 | 
						// Ensure email is nice
 | 
				
			||||||
	m.Email = strings.TrimSpace(strings.ToLower(m.Email))
 | 
						m.Email = strings.TrimSpace(strings.ToLower(m.Email))
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if m.IsSystem {
 | 
						if m.IsSystem {
 | 
				
			||||||
		return errors.ErrSystemUserReadonly
 | 
							return errors.ErrSystemUserReadonly
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.ID == 0 {
 | 
						db := database.GetDB()
 | 
				
			||||||
		m.ID, err = Create(m)
 | 
						// todo: touch? not sure that save does this or not?
 | 
				
			||||||
	} else {
 | 
						result := db.Save(m)
 | 
				
			||||||
		err = Update(m)
 | 
						return result.Error
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete will mark a user as deleted
 | 
					// Delete will mark a user as deleted
 | 
				
			||||||
func (m *Model) Delete() bool {
 | 
					func (m *Model) Delete() bool {
 | 
				
			||||||
	m.Touch(false)
 | 
						if m.ID == 0 {
 | 
				
			||||||
	m.IsDeleted = true
 | 
							// Can't delete a new object
 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
					 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
						result := db.Delete(m)
 | 
				
			||||||
 | 
						return result.Error == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetPermissions will wipe out any existing permissions and add new ones for this user
 | 
					// SetPermissions will wipe out any existing permissions and add new ones for this user
 | 
				
			||||||
@@ -103,30 +105,20 @@ func (m *Model) SetPermissions(permissions []string) error {
 | 
				
			|||||||
		return eris.Errorf("Cannot set permissions without first saving the User")
 | 
							return eris.Errorf("Cannot set permissions without first saving the User")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Wipe out previous permissions
 | 
						// Wipe out previous permissions
 | 
				
			||||||
	query := `DELETE FROM "user_has_capability" WHERE "user_id" = ?`
 | 
						if result := db.Where("user_id = ?", m.ID).Delete(&UserHasCapabilityModel{}); result.Error != nil {
 | 
				
			||||||
	if _, err := db.Exec(query, m.ID); err != nil {
 | 
							return result.Error
 | 
				
			||||||
		logger.Debug("QUERY: %v -- %v", query, m.ID)
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(permissions) > 0 {
 | 
						if len(permissions) > 0 {
 | 
				
			||||||
		// Add new permissions
 | 
							// Add new permissions
 | 
				
			||||||
 | 
							objs := []*UserHasCapabilityModel{}
 | 
				
			||||||
		for _, permission := range permissions {
 | 
							for _, permission := range permissions {
 | 
				
			||||||
			query = `INSERT INTO "user_has_capability" (
 | 
								objs = append(objs, &UserHasCapabilityModel{UserID: m.ID, CapabilityName: permission})
 | 
				
			||||||
				"user_id", "capability_id"
 | 
					 | 
				
			||||||
			) VALUES (
 | 
					 | 
				
			||||||
				?,
 | 
					 | 
				
			||||||
				(SELECT id FROM capability WHERE name = ?)
 | 
					 | 
				
			||||||
			)`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			_, err := db.Exec(query, m.ID, permission)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				logger.Debug("QUERY: %v -- %v -- %v", query, m.ID, permission)
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if result := db.Create(objs); result.Error != nil {
 | 
				
			||||||
 | 
								return result.Error
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -164,21 +156,18 @@ func (m *Model) SaveCapabilities() error {
 | 
				
			|||||||
		return eris.New("At least 1 capability required for a user")
 | 
							return eris.New("At least 1 capability required for a user")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db := database.GetInstance()
 | 
						db := database.GetDB()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get a full list of capabilities
 | 
						// Get a full list of capabilities
 | 
				
			||||||
	var capabilities []string
 | 
						var capabilities []entity.Capability
 | 
				
			||||||
	query := `SELECT "name" from "capability"`
 | 
						if result := db.Find(&capabilities); result.Error != nil {
 | 
				
			||||||
	err := db.Select(&capabilities, query)
 | 
							return result.Error
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check that the capabilities defined exist in the db
 | 
						// Check that the capabilities defined exist in the db
 | 
				
			||||||
	for _, cap := range m.Capabilities {
 | 
						for _, cap := range m.Capabilities {
 | 
				
			||||||
		found := false
 | 
							found := false
 | 
				
			||||||
		for _, a := range capabilities {
 | 
							for _, a := range capabilities {
 | 
				
			||||||
			if a == cap {
 | 
								if a.Name == cap {
 | 
				
			||||||
				found = true
 | 
									found = true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +0,0 @@
 | 
				
			|||||||
package user
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"npm/internal/model"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ListResponse is the JSON response for users list
 | 
					 | 
				
			||||||
type ListResponse struct {
 | 
					 | 
				
			||||||
	Total  int            `json:"total"`
 | 
					 | 
				
			||||||
	Offset int            `json:"offset"`
 | 
					 | 
				
			||||||
	Limit  int            `json:"limit"`
 | 
					 | 
				
			||||||
	Sort   []model.Sort   `json:"sort"`
 | 
					 | 
				
			||||||
	Filter []model.Filter `json:"filter,omitempty"`
 | 
					 | 
				
			||||||
	Items  []Model        `json:"items,omitempty"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -12,7 +12,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// UserJWTClaims is the structure of a JWT for a User
 | 
					// UserJWTClaims is the structure of a JWT for a User
 | 
				
			||||||
type UserJWTClaims struct {
 | 
					type UserJWTClaims struct {
 | 
				
			||||||
	UserID int      `json:"uid"`
 | 
						UserID uint     `json:"uid"`
 | 
				
			||||||
	Roles  []string `json:"roles"`
 | 
						Roles  []string `json:"roles"`
 | 
				
			||||||
	jwt.StandardClaims
 | 
						jwt.StandardClaims
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,8 @@
 | 
				
			|||||||
package model
 | 
					package model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PageInfo is the model used by Api Handlers and passed on to other parts
 | 
					// PageInfo is the model used by Api Handlers and passed on to other parts
 | 
				
			||||||
// of the application
 | 
					// of the application
 | 
				
			||||||
type PageInfo struct {
 | 
					type PageInfo struct {
 | 
				
			||||||
	FromDate time.Time `json:"from_date"`
 | 
					 | 
				
			||||||
	ToDate   time.Time `json:"to_date"`
 | 
					 | 
				
			||||||
	Sort   []Sort   `json:"sort"`
 | 
						Sort   []Sort   `json:"sort"`
 | 
				
			||||||
	Offset int      `json:"offset"`
 | 
						Offset int      `json:"offset"`
 | 
				
			||||||
	Limit  int      `json:"limit"`
 | 
						Limit  int      `json:"limit"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,9 +49,10 @@ func ConfigureHost(h host.Model) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	removeHostFiles(h)
 | 
						removeHostFiles(h)
 | 
				
			||||||
	filename := getHostFilename(h, "")
 | 
						filename := getHostFilename(h, "")
 | 
				
			||||||
	if h.IsDeleted {
 | 
						// if h.IsDeleted {
 | 
				
			||||||
		filename = getHostFilename(h, DeletedSuffix)
 | 
						//	filename = getHostFilename(h, DeletedSuffix)
 | 
				
			||||||
	} else if h.IsDisabled {
 | 
						// } else if h.IsDisabled {
 | 
				
			||||||
 | 
						if h.IsDisabled {
 | 
				
			||||||
		filename = getHostFilename(h, DisabledSuffix)
 | 
							filename = getHostFilename(h, DisabledSuffix)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,7 +74,7 @@ func ConfigureHost(h host.Model) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Write the .error file, if this isn't a deleted or disabled host
 | 
							// Write the .error file, if this isn't a deleted or disabled host
 | 
				
			||||||
		// as the reload will only fail because of this host, if it's enabled
 | 
							// as the reload will only fail because of this host, if it's enabled
 | 
				
			||||||
		if !h.IsDeleted && !h.IsDisabled {
 | 
							if !h.IsDisabled {
 | 
				
			||||||
			filename = getHostFilename(h, ErrorSuffix)
 | 
								filename = getHostFilename(h, ErrorSuffix)
 | 
				
			||||||
			// Clear existing file(s) again
 | 
								// Clear existing file(s) again
 | 
				
			||||||
			removeHostFiles(h)
 | 
								removeHostFiles(h)
 | 
				
			||||||
@@ -108,9 +109,9 @@ func ConfigureUpstream(u upstream.Model) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	removeUpstreamFiles(u)
 | 
						removeUpstreamFiles(u)
 | 
				
			||||||
	filename := getUpstreamFilename(u, "")
 | 
						filename := getUpstreamFilename(u, "")
 | 
				
			||||||
	if u.IsDeleted {
 | 
						// if u.IsDeleted {
 | 
				
			||||||
		filename = getUpstreamFilename(u, DeletedSuffix)
 | 
						// 	filename = getUpstreamFilename(u, DeletedSuffix)
 | 
				
			||||||
	}
 | 
						// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Write the config to disk
 | 
						// Write the config to disk
 | 
				
			||||||
	err := writeTemplate(filename, u.NginxTemplate.Template, data, "")
 | 
						err := writeTemplate(filename, u.NginxTemplate.Template, data, "")
 | 
				
			||||||
@@ -130,14 +131,14 @@ func ConfigureUpstream(u upstream.Model) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Write the .error file, if this isn't a deleted upstream
 | 
							// Write the .error file, if this isn't a deleted upstream
 | 
				
			||||||
		// as the reload will only fail because of this upstream
 | 
							// as the reload will only fail because of this upstream
 | 
				
			||||||
		if !u.IsDeleted {
 | 
							// if !u.IsDeleted {
 | 
				
			||||||
		filename = getUpstreamFilename(u, ErrorSuffix)
 | 
							filename = getUpstreamFilename(u, ErrorSuffix)
 | 
				
			||||||
		// Clear existing file(s) again
 | 
							// Clear existing file(s) again
 | 
				
			||||||
		removeUpstreamFiles(u)
 | 
							removeUpstreamFiles(u)
 | 
				
			||||||
		// Write the template again, but with an error message at the end of the file
 | 
							// Write the template again, but with an error message at the end of the file
 | 
				
			||||||
		// nolint: errcheck, gosec
 | 
							// nolint: errcheck, gosec
 | 
				
			||||||
		writeTemplate(filename, u.NginxTemplate.Template, data, u.ErrorMessage)
 | 
							writeTemplate(filename, u.NginxTemplate.Template, data, u.ErrorMessage)
 | 
				
			||||||
		}
 | 
							// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		logger.Debug(u.ErrorMessage)
 | 
							logger.Debug(u.ErrorMessage)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@@ -195,9 +196,9 @@ func GetHostConfigContent(h host.Model) (string, error) {
 | 
				
			|||||||
	if h.IsDisabled {
 | 
						if h.IsDisabled {
 | 
				
			||||||
		filename = getHostFilename(h, DisabledSuffix)
 | 
							filename = getHostFilename(h, DisabledSuffix)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if h.IsDeleted {
 | 
						// if h.IsDeleted {
 | 
				
			||||||
		filename = getHostFilename(h, DeletedSuffix)
 | 
						// 	filename = getHostFilename(h, DeletedSuffix)
 | 
				
			||||||
	}
 | 
						// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// nolint: gosec
 | 
						// nolint: gosec
 | 
				
			||||||
	cnt, err := os.ReadFile(filename)
 | 
						cnt, err := os.ReadFile(filename)
 | 
				
			||||||
@@ -213,9 +214,9 @@ func GetUpstreamConfigContent(u upstream.Model) (string, error) {
 | 
				
			|||||||
	if u.ErrorMessage != "" {
 | 
						if u.ErrorMessage != "" {
 | 
				
			||||||
		filename = getUpstreamFilename(u, ErrorSuffix)
 | 
							filename = getUpstreamFilename(u, ErrorSuffix)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if u.IsDeleted {
 | 
						// if u.IsDeleted {
 | 
				
			||||||
		filename = getUpstreamFilename(u, DeletedSuffix)
 | 
						// 	filename = getUpstreamFilename(u, DeletedSuffix)
 | 
				
			||||||
	}
 | 
						// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// nolint: gosec
 | 
						// nolint: gosec
 | 
				
			||||||
	cnt, err := os.ReadFile(filename)
 | 
						cnt, err := os.ReadFile(filename)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ package nginx
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/entity/certificate"
 | 
						"npm/internal/entity/certificate"
 | 
				
			||||||
	"npm/internal/entity/host"
 | 
						"npm/internal/entity/host"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,7 +46,9 @@ server {
 | 
				
			|||||||
				IsDisabled: false,
 | 
									IsDisabled: false,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			cert: certificate.Model{
 | 
								cert: certificate.Model{
 | 
				
			||||||
 | 
									ModelBase: entity.ModelBase{
 | 
				
			||||||
					ID: 77,
 | 
										ID: 77,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
				Status:                 certificate.StatusProvided,
 | 
									Status:                 certificate.StatusProvided,
 | 
				
			||||||
				Type:                   certificate.TypeHTTP,
 | 
									Type:                   certificate.TypeHTTP,
 | 
				
			||||||
				CertificateAuthorityID: 99,
 | 
									CertificateAuthorityID: 99,
 | 
				
			||||||
@@ -61,7 +64,9 @@ server {
 | 
				
			|||||||
				IsDisabled: false,
 | 
									IsDisabled: false,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			cert: certificate.Model{
 | 
								cert: certificate.Model{
 | 
				
			||||||
 | 
									ModelBase: entity.ModelBase{
 | 
				
			||||||
					ID: 66,
 | 
										ID: 66,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
				Status: certificate.StatusProvided,
 | 
									Status: certificate.StatusProvided,
 | 
				
			||||||
				Type:   certificate.TypeCustom,
 | 
									Type:   certificate.TypeCustom,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,6 +58,7 @@ build_backend() {
 | 
				
			|||||||
		-w '/app/backend' \
 | 
							-w '/app/backend' \
 | 
				
			||||||
		"${IMAGE}" \
 | 
							"${IMAGE}" \
 | 
				
			||||||
		go build \
 | 
							go build \
 | 
				
			||||||
 | 
								-tags 'json1' \
 | 
				
			||||||
			-buildvcs=false \
 | 
								-buildvcs=false \
 | 
				
			||||||
			-ldflags "-w -s -X main.commit=${BUILD_COMMIT:-notset} -X main.version=${BUILD_VERSION} -X main.sentryDSN=${SENTRY_DSN:-}" \
 | 
								-ldflags "-w -s -X main.commit=${BUILD_COMMIT:-notset} -X main.version=${BUILD_VERSION} -X main.sentryDSN=${SENTRY_DSN:-}" \
 | 
				
			||||||
			-o "/app/bin/$FILENAME" \
 | 
								-o "/app/bin/$FILENAME" \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ echo -e "${BLUE}❯ ${CYAN}Building binaries for ${YELLOW}${GOARCH} (${TARGETPLA
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# server
 | 
					# server
 | 
				
			||||||
go build \
 | 
					go build \
 | 
				
			||||||
 | 
						-tags 'json1' \
 | 
				
			||||||
	-buildvcs=false \
 | 
						-buildvcs=false \
 | 
				
			||||||
	-ldflags "-w -s -X main.commit=${BUILD_COMMIT:-notset} -X main.version=${BUILD_VERSION} -X main.sentryDSN=${SENTRY_DSN:-}" \
 | 
						-ldflags "-w -s -X main.commit=${BUILD_COMMIT:-notset} -X main.version=${BUILD_VERSION} -X main.sentryDSN=${SENTRY_DSN:-}" \
 | 
				
			||||||
	-o "${1:-/dist/server}" \
 | 
						-o "${1:-/dist/server}" \
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user