mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-11-04 01:15:14 +00:00 
			
		
		
		
	More work to support nullable foreign keys
Adds nullable int/uint types
This commit is contained in:
		@@ -116,7 +116,7 @@ CREATE TABLE IF NOT EXISTS `certificate`
 | 
				
			|||||||
	`dns_provider_id` INT, -- 0, for a http or custom cert
 | 
						`dns_provider_id` INT, -- 0, for a http or custom cert
 | 
				
			||||||
	`name` VARCHAR(50) NOT NULL,
 | 
						`name` VARCHAR(50) NOT NULL,
 | 
				
			||||||
	`domain_names` TEXT NOT NULL,
 | 
						`domain_names` TEXT NOT NULL,
 | 
				
			||||||
	`expires_on` INT DEFAULT 0,
 | 
						`expires_on` BIGINT NOT NULL DEFAULT 0,
 | 
				
			||||||
	`status` VARCHAR(50) NOT NULL, -- ready,requesting,failed,provided
 | 
						`status` VARCHAR(50) NOT NULL, -- ready,requesting,failed,provided
 | 
				
			||||||
	`error_message` TEXT NOT NULL,
 | 
						`error_message` TEXT NOT NULL,
 | 
				
			||||||
	`meta` TEXT NOT NULL,
 | 
						`meta` TEXT NOT NULL,
 | 
				
			||||||
@@ -205,7 +205,7 @@ CREATE TABLE IF NOT EXISTS `access_list`
 | 
				
			|||||||
	FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS host
 | 
					CREATE TABLE IF NOT EXISTS `host`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	`id` INT AUTO_INCREMENT PRIMARY KEY,
 | 
						`id` INT AUTO_INCREMENT PRIMARY KEY,
 | 
				
			||||||
	`created_at` BIGINT NOT NULL DEFAULT 0,
 | 
						`created_at` BIGINT NOT NULL DEFAULT 0,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,7 +102,7 @@ CREATE TABLE "certificate" (
 | 
				
			|||||||
	"dns_provider_id" INTEGER REFERENCES "dns_provider"("id") ON DELETE CASCADE, -- 0, for a http or custom cert
 | 
						"dns_provider_id" INTEGER REFERENCES "dns_provider"("id") ON DELETE CASCADE, -- 0, for a http or custom cert
 | 
				
			||||||
	"name" VARCHAR(50) NOT NULL,
 | 
						"name" VARCHAR(50) NOT NULL,
 | 
				
			||||||
	"domain_names" TEXT NOT NULL,
 | 
						"domain_names" TEXT NOT NULL,
 | 
				
			||||||
	"expires_on" INTEGER DEFAULT 0,
 | 
						"expires_on" BIGINT NOT NULL DEFAULT 0,
 | 
				
			||||||
	"status" VARCHAR(50) NOT NULL, -- ready,requesting,failed,provided
 | 
						"status" VARCHAR(50) NOT NULL, -- ready,requesting,failed,provided
 | 
				
			||||||
	"error_message" TEXT NOT NULL DEFAULT '',
 | 
						"error_message" TEXT NOT NULL DEFAULT '',
 | 
				
			||||||
	"meta" TEXT NOT NULL,
 | 
						"meta" TEXT NOT NULL,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,243 +2,243 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `jwt_keys`
 | 
					CREATE TABLE IF NOT EXISTS `jwt_keys`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted INTEGER NOT NULL DEFAULT 0,
 | 
						`is_deleted` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	public_key TEXT NOT NULL,
 | 
						`public_key` TEXT NOT NULL,
 | 
				
			||||||
	private_key TEXT NOT NULL
 | 
						`private_key` TEXT NOT NULL
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `user`
 | 
					CREATE TABLE IF NOT EXISTS `user`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `capability`
 | 
					CREATE TABLE IF NOT EXISTS `capability`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	name TEXT PRIMARY KEY,
 | 
						`name` TEXT PRIMARY KEY,
 | 
				
			||||||
	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_name TEXT NOT NULL,
 | 
						`capability_name` TEXT NOT NULL,
 | 
				
			||||||
	UNIQUE (user_id, capability_name),
 | 
						UNIQUE (`user_id`, `capability_name`),
 | 
				
			||||||
	FOREIGN KEY (capability_name) REFERENCES capability (name)
 | 
						FOREIGN KEY (`capability_name`) REFERENCES `capability` (`name`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `auth`
 | 
					CREATE TABLE IF NOT EXISTS `auth`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	UNIQUE (user_id, type)
 | 
						UNIQUE (`user_id`, `type`)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `setting`
 | 
					CREATE TABLE IF NOT EXISTS `setting`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	UNIQUE (name)
 | 
						UNIQUE (`name`)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	action TEXT NOT NULL,
 | 
						`action` TEXT NOT NULL,
 | 
				
			||||||
	meta TEXT NOT NULL,
 | 
						`meta` TEXT NOT NULL,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `certificate`
 | 
					CREATE TABLE IF NOT EXISTS `certificate`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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, -- custom,dns,http
 | 
						`type` TEXT NOT NULL, -- custom,dns,http
 | 
				
			||||||
	certificate_authority_id INTEGER, -- 0 for a custom cert
 | 
						`certificate_authority_id` INTEGER, -- 0 for a custom cert
 | 
				
			||||||
	dns_provider_id INTEGER, -- 0, for a http or custom cert
 | 
						`dns_provider_id` INTEGER, -- 0, for a http or custom cert
 | 
				
			||||||
	name TEXT NOT NULL,
 | 
						`name` TEXT NOT NULL,
 | 
				
			||||||
	domain_names TEXT NOT NULL,
 | 
						`domain_names` TEXT NOT NULL,
 | 
				
			||||||
	expires_on INTEGER DEFAULT 0,
 | 
						`expires_on` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	status TEXT NOT NULL, -- ready,requesting,failed,provided
 | 
						`status` TEXT NOT NULL, -- ready,requesting,failed,provided
 | 
				
			||||||
	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,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	FOREIGN KEY (certificate_authority_id) REFERENCES certificate_authority (id),
 | 
						FOREIGN KEY (`certificate_authority_id`) REFERENCES `certificate_authority` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	FOREIGN KEY (dns_provider_id) REFERENCES dns_provider (id)
 | 
						FOREIGN KEY (`dns_provider_id`) REFERENCES `dns_provider` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `stream`
 | 
					CREATE TABLE IF NOT EXISTS `stream`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	tcp_forwarding INTEGER NOT NULL DEFAULT 0,
 | 
						`tcp_forwarding` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	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,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `upstream`
 | 
					CREATE TABLE IF NOT EXISTS `upstream`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	ip_hash INTEGER NOT NULL DEFAULT 0,
 | 
						`ip_hash` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	ntlm INTEGER NOT NULL DEFAULT 0,
 | 
						`ntlm` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	keepalive INTEGER NOT NULL DEFAULT 0,
 | 
						`keepalive` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	keepalive_requests INTEGER NOT NULL DEFAULT 0,
 | 
						`keepalive_requests` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	keepalive_time TEXT NOT NULL DEFAULT "",
 | 
						`keepalive_time` TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	keepalive_timeout TEXT NOT NULL DEFAULT "",
 | 
						`keepalive_timeout` TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	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 "",
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	FOREIGN KEY (nginx_template_id) REFERENCES nginx_template (id)
 | 
						FOREIGN KEY (`nginx_template_id`) REFERENCES `nginx_template` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	is_backup INTEGER NOT NULL DEFAULT 0,
 | 
						`is_backup` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	FOREIGN KEY (upstream_id) REFERENCES upstream (id)
 | 
						FOREIGN KEY (`upstream_id`) REFERENCES `upstream` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id)
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS `host`
 | 
					CREATE TABLE IF NOT EXISTS `host`
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
	id INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
						`id` INTEGER PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
	created_at INTEGER NOT NULL DEFAULT 0,
 | 
						`created_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	updated_at INTEGER NOT NULL DEFAULT 0,
 | 
						`updated_at` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	is_deleted 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,
 | 
				
			||||||
	listen_interface TEXT NOT NULL DEFAULT "",
 | 
						`listen_interface` TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	domain_names TEXT NOT NULL,
 | 
						`domain_names` TEXT NOT NULL,
 | 
				
			||||||
	upstream_id INTEGER,
 | 
						`upstream_id` INTEGER,
 | 
				
			||||||
	proxy_scheme TEXT NOT NULL DEFAULT "",
 | 
						`proxy_scheme` TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	proxy_host TEXT NOT NULL DEFAULT "",
 | 
						`proxy_host` TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	proxy_port INTEGER NOT NULL DEFAULT 0,
 | 
						`proxy_port` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	certificate_id INTEGER,
 | 
						`certificate_id` INTEGER,
 | 
				
			||||||
	access_list_id INTEGER,
 | 
						`access_list_id` INTEGER,
 | 
				
			||||||
	ssl_forced INTEGER NOT NULL DEFAULT 0,
 | 
						`ssl_forced` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	caching_enabled INTEGER NOT NULL DEFAULT 0,
 | 
						`caching_enabled` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	block_exploits INTEGER NOT NULL DEFAULT 0,
 | 
						`block_exploits` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	allow_websocket_upgrade INTEGER NOT NULL DEFAULT 0,
 | 
						`allow_websocket_upgrade` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	http2_support INTEGER NOT NULL DEFAULT 0,
 | 
						`http2_support` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	hsts_enabled INTEGER NOT NULL DEFAULT 0,
 | 
						`hsts_enabled` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	hsts_subdomains INTEGER NOT NULL DEFAULT 0,
 | 
						`hsts_subdomains` INTEGER NOT NULL DEFAULT 0,
 | 
				
			||||||
	paths TEXT NOT NULL DEFAULT "",
 | 
						`paths` TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	advanced_config TEXT NOT NULL DEFAULT "",
 | 
						`advanced_config` TEXT NOT NULL DEFAULT "",
 | 
				
			||||||
	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,
 | 
				
			||||||
	FOREIGN KEY (user_id) REFERENCES user (id),
 | 
						FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	FOREIGN KEY (nginx_template_id) REFERENCES nginx_template (id),
 | 
						FOREIGN KEY (`nginx_template_id`) REFERENCES `nginx_template` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	FOREIGN KEY (upstream_id) REFERENCES upstream (id),
 | 
						FOREIGN KEY (`upstream_id`) REFERENCES `upstream` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	FOREIGN KEY (certificate_id) REFERENCES certificate (id),
 | 
						FOREIGN KEY (`certificate_id`) REFERENCES `certificate` (`id`) ON DELETE CASCADE,
 | 
				
			||||||
	FOREIGN KEY (access_list_id) REFERENCES access_list (id)
 | 
						FOREIGN KEY (`access_list_id`) REFERENCES `access_list` (`id`) ON DELETE CASCADE
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- migrate:down
 | 
					-- migrate:down
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -148,7 +148,7 @@ func getCertificateFromRequest(w http.ResponseWriter, r *http.Request) *certific
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getCertificateFromRequest has some reusable code for all endpoints that
 | 
					// fillObjectFromBody has some reusable code for all endpoints that
 | 
				
			||||||
// 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 fillObjectFromBody(w http.ResponseWriter, r *http.Request, validationSchema string, o interface{}) bool {
 | 
					func fillObjectFromBody(w http.ResponseWriter, r *http.Request, validationSchema string, o interface{}) bool {
 | 
				
			||||||
	bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
 | 
						bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
 | 
				
			||||||
@@ -167,6 +167,7 @@ func fillObjectFromBody(w http.ResponseWriter, r *http.Request, validationSchema
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	err := json.Unmarshal(bodyBytes, o)
 | 
						err := json.Unmarshal(bodyBytes, o)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Debug("Unmarshal Error: %+v", err)
 | 
				
			||||||
		h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
 | 
							h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,7 +87,7 @@ func CreateHost() func(http.ResponseWriter, *http.Request) {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if newHost.UpstreamID > 0 {
 | 
							if newHost.UpstreamID.Uint > 0 {
 | 
				
			||||||
			// nolint: errcheck, gosec
 | 
								// nolint: errcheck, gosec
 | 
				
			||||||
			newHost.Expand([]string{"upstream"})
 | 
								newHost.Expand([]string{"upstream"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,27 +56,19 @@ func connect() (*gorm.DB, error) {
 | 
				
			|||||||
		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 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
 | 
						// see: https://gorm.io/docs/gorm_config.html
 | 
				
			||||||
 | 
						cfg := gorm.Config{
 | 
				
			||||||
		NamingStrategy: schema.NamingStrategy{
 | 
							NamingStrategy: schema.NamingStrategy{
 | 
				
			||||||
			SingularTable: true,
 | 
								SingularTable: true,
 | 
				
			||||||
			NoLowerCase:   true,
 | 
								NoLowerCase:   true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		PrepareStmt: false,
 | 
							PrepareStmt: false,
 | 
				
			||||||
		Logger:      gormlogger.Default.LogMode(gormlogger.Silent),
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
						// Silence gorm query errors unless when not in debug mode
 | 
				
			||||||
func autocreate(dbFile string) {
 | 
						if config.GetLogLevel() != logger.DebugLevel {
 | 
				
			||||||
	if _, err := os.Stat(dbFile); os.IsNotExist(err) {
 | 
							cfg.Logger = gormlogger.Default.LogMode(gormlogger.Silent)
 | 
				
			||||||
		// Create it
 | 
					 | 
				
			||||||
		logger.Info("Creating Sqlite DB: %s", dbFile)
 | 
					 | 
				
			||||||
		// nolint: gosec
 | 
					 | 
				
			||||||
		_, err = os.Create(dbFile)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			logger.Error("FileCreateError", err)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return gorm.Open(d, &cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,13 +45,13 @@ const (
 | 
				
			|||||||
// Model is the model
 | 
					// Model is the model
 | 
				
			||||||
type Model struct {
 | 
					type Model struct {
 | 
				
			||||||
	entity.ModelBase
 | 
						entity.ModelBase
 | 
				
			||||||
	ExpiresOn              types.NullableDBDate `json:"expires_on" gorm:"column:expires_on" filter:"expires_on,integer"`
 | 
					 | 
				
			||||||
	Type                   string               `json:"type" gorm:"column:type" filter:"type,string"`
 | 
					 | 
				
			||||||
	UserID                 uint                 `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
						UserID                 uint                 `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
 | 
				
			||||||
	CertificateAuthorityID uint                 `json:"certificate_authority_id" gorm:"column:certificate_authority_id" filter:"certificate_authority_id,integer"`
 | 
						Type                   string               `json:"type" gorm:"column:type" filter:"type,string"`
 | 
				
			||||||
	DNSProviderID          uint                 `json:"dns_provider_id" gorm:"column:dns_provider_id" filter:"dns_provider_id,integer"`
 | 
						CertificateAuthorityID types.NullableDBUint `json:"certificate_authority_id" gorm:"column:certificate_authority_id" filter:"certificate_authority_id,integer"`
 | 
				
			||||||
 | 
						DNSProviderID          types.NullableDBUint `json:"dns_provider_id" gorm:"column:dns_provider_id" filter:"dns_provider_id,integer"`
 | 
				
			||||||
	Name                   string               `json:"name" gorm:"column:name" filter:"name,string"`
 | 
						Name                   string               `json:"name" gorm:"column:name" filter:"name,string"`
 | 
				
			||||||
	DomainNames            types.JSONB          `json:"domain_names" gorm:"column:domain_names" filter:"domain_names,string"`
 | 
						DomainNames            types.JSONB          `json:"domain_names" gorm:"column:domain_names" filter:"domain_names,string"`
 | 
				
			||||||
 | 
						ExpiresOn              int64                `json:"expires_on" gorm:"column:expires_on" filter:"expires_on,integer"`
 | 
				
			||||||
	Status                 string               `json:"status" gorm:"column:status" filter:"status,string"`
 | 
						Status                 string               `json:"status" gorm:"column:status" filter:"status,string"`
 | 
				
			||||||
	ErrorMessage           string               `json:"error_message" gorm:"column:error_message" filter:"error_message,string"`
 | 
						ErrorMessage           string               `json:"error_message" gorm:"column:error_message" filter:"error_message,string"`
 | 
				
			||||||
	Meta                   types.JSONB          `json:"-" gorm:"column:meta"`
 | 
						Meta                   types.JSONB          `json:"-" gorm:"column:meta"`
 | 
				
			||||||
@@ -117,13 +117,13 @@ func (m *Model) Validate() bool {
 | 
				
			|||||||
	switch m.Type {
 | 
						switch m.Type {
 | 
				
			||||||
	case TypeCustom:
 | 
						case TypeCustom:
 | 
				
			||||||
		// TODO: make sure meta contains required fields
 | 
							// TODO: make sure meta contains required fields
 | 
				
			||||||
		return m.DNSProviderID == 0 && m.CertificateAuthorityID == 0
 | 
							return m.DNSProviderID.Uint == 0 && m.CertificateAuthorityID.Uint == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case TypeHTTP:
 | 
						case TypeHTTP:
 | 
				
			||||||
		return m.DNSProviderID == 0 && m.CertificateAuthorityID > 0
 | 
							return m.DNSProviderID.Uint == 0 && m.CertificateAuthorityID.Uint > 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case TypeDNS:
 | 
						case TypeDNS:
 | 
				
			||||||
		return m.DNSProviderID > 0 && m.CertificateAuthorityID > 0
 | 
							return m.DNSProviderID.Uint > 0 && m.CertificateAuthorityID.Uint > 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case TypeMkcert:
 | 
						case TypeMkcert:
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
@@ -175,15 +175,15 @@ func (m *Model) setDefaultStatus() {
 | 
				
			|||||||
func (m *Model) Expand(items []string) error {
 | 
					func (m *Model) Expand(items []string) error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if util.SliceContainsItem(items, "certificate-authority") && m.CertificateAuthorityID > 0 {
 | 
						if util.SliceContainsItem(items, "certificate-authority") && m.CertificateAuthorityID.Uint > 0 {
 | 
				
			||||||
		var certificateAuthority certificateauthority.Model
 | 
							var certificateAuthority certificateauthority.Model
 | 
				
			||||||
		certificateAuthority, err = certificateauthority.GetByID(m.CertificateAuthorityID)
 | 
							certificateAuthority, err = certificateauthority.GetByID(m.CertificateAuthorityID.Uint)
 | 
				
			||||||
		m.CertificateAuthority = &certificateAuthority
 | 
							m.CertificateAuthority = &certificateAuthority
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if util.SliceContainsItem(items, "dns-provider") && m.DNSProviderID > 0 {
 | 
						if util.SliceContainsItem(items, "dns-provider") && m.DNSProviderID.Uint > 0 {
 | 
				
			||||||
		var dnsProvider dnsprovider.Model
 | 
							var dnsProvider dnsprovider.Model
 | 
				
			||||||
		dnsProvider, err = dnsprovider.GetByID(m.DNSProviderID)
 | 
							dnsProvider, err = dnsprovider.GetByID(m.DNSProviderID.Uint)
 | 
				
			||||||
		m.DNSProvider = &dnsProvider
 | 
							m.DNSProvider = &dnsProvider
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -262,8 +262,7 @@ func (m *Model) Request() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// If done
 | 
						// If done
 | 
				
			||||||
	m.Status = StatusProvided
 | 
						m.Status = StatusProvided
 | 
				
			||||||
	t := time.Now()
 | 
						m.ExpiresOn = time.Now().UnixMilli()
 | 
				
			||||||
	m.ExpiresOn.Time = &t // todo
 | 
					 | 
				
			||||||
	if err := m.Save(); err != nil {
 | 
						if err := m.Save(); err != nil {
 | 
				
			||||||
		logger.Error("CertificateSaveError", err)
 | 
							logger.Error("CertificateSaveError", err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -287,11 +286,11 @@ func (m *Model) GetTemplate() Template {
 | 
				
			|||||||
		ID:                     m.ID,
 | 
							ID:                     m.ID,
 | 
				
			||||||
		CreatedAt:              fmt.Sprintf("%d", m.CreatedAt), // todo: nice date string
 | 
							CreatedAt:              fmt.Sprintf("%d", m.CreatedAt), // todo: nice date string
 | 
				
			||||||
		UpdatedAt:              fmt.Sprintf("%d", m.UpdatedAt), // todo: nice date string
 | 
							UpdatedAt:              fmt.Sprintf("%d", m.UpdatedAt), // todo: nice date string
 | 
				
			||||||
		ExpiresOn:              m.ExpiresOn.AsString(),
 | 
							ExpiresOn:              util.UnixMilliToNiceFormat(m.ExpiresOn),
 | 
				
			||||||
		Type:                   m.Type,
 | 
							Type:                   m.Type,
 | 
				
			||||||
		UserID:                 m.UserID,
 | 
							UserID:                 m.UserID,
 | 
				
			||||||
		CertificateAuthorityID: m.CertificateAuthorityID,
 | 
							CertificateAuthorityID: m.CertificateAuthorityID.Uint,
 | 
				
			||||||
		DNSProviderID:          m.DNSProviderID,
 | 
							DNSProviderID:          m.DNSProviderID.Uint,
 | 
				
			||||||
		Name:                   m.Name,
 | 
							Name:                   m.Name,
 | 
				
			||||||
		DomainNames:            domainNames,
 | 
							DomainNames:            domainNames,
 | 
				
			||||||
		Status:                 m.Status,
 | 
							Status:                 m.Status,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,12 +32,12 @@ type Model struct {
 | 
				
			|||||||
	NginxTemplateID       uint                 `json:"nginx_template_id" gorm:"column:nginx_template_id" filter:"nginx_template_id,integer"`
 | 
						NginxTemplateID       uint                 `json:"nginx_template_id" gorm:"column:nginx_template_id" filter:"nginx_template_id,integer"`
 | 
				
			||||||
	ListenInterface       string               `json:"listen_interface" gorm:"column:listen_interface" filter:"listen_interface,string"`
 | 
						ListenInterface       string               `json:"listen_interface" gorm:"column:listen_interface" filter:"listen_interface,string"`
 | 
				
			||||||
	DomainNames           types.JSONB          `json:"domain_names" gorm:"column:domain_names" filter:"domain_names,string"`
 | 
						DomainNames           types.JSONB          `json:"domain_names" gorm:"column:domain_names" filter:"domain_names,string"`
 | 
				
			||||||
	UpstreamID            uint        `json:"upstream_id" gorm:"column:upstream_id" filter:"upstream_id,integer"`
 | 
						UpstreamID            types.NullableDBUint `json:"upstream_id" gorm:"column:upstream_id" filter:"upstream_id,integer"`
 | 
				
			||||||
	ProxyScheme           string               `json:"proxy_scheme" gorm:"column:proxy_scheme" filter:"proxy_scheme,string"`
 | 
						ProxyScheme           string               `json:"proxy_scheme" gorm:"column:proxy_scheme" filter:"proxy_scheme,string"`
 | 
				
			||||||
	ProxyHost             string               `json:"proxy_host" gorm:"column:proxy_host" filter:"proxy_host,string"`
 | 
						ProxyHost             string               `json:"proxy_host" gorm:"column:proxy_host" filter:"proxy_host,string"`
 | 
				
			||||||
	ProxyPort             int                  `json:"proxy_port" gorm:"column:proxy_port" filter:"proxy_port,integer"`
 | 
						ProxyPort             int                  `json:"proxy_port" gorm:"column:proxy_port" filter:"proxy_port,integer"`
 | 
				
			||||||
	CertificateID         uint        `json:"certificate_id" gorm:"column:certificate_id" filter:"certificate_id,integer"`
 | 
						CertificateID         types.NullableDBUint `json:"certificate_id" gorm:"column:certificate_id" filter:"certificate_id,integer"`
 | 
				
			||||||
	AccessListID          uint        `json:"access_list_id" gorm:"column:access_list_id" filter:"access_list_id,integer"`
 | 
						AccessListID          types.NullableDBUint `json:"access_list_id" gorm:"column:access_list_id" filter:"access_list_id,integer"`
 | 
				
			||||||
	SSLForced             bool                 `json:"ssl_forced" gorm:"column:ssl_forced" filter:"ssl_forced,boolean"`
 | 
						SSLForced             bool                 `json:"ssl_forced" gorm:"column:ssl_forced" filter:"ssl_forced,boolean"`
 | 
				
			||||||
	CachingEnabled        bool                 `json:"caching_enabled" gorm:"column:caching_enabled" filter:"caching_enabled,boolean"`
 | 
						CachingEnabled        bool                 `json:"caching_enabled" gorm:"column:caching_enabled" filter:"caching_enabled,boolean"`
 | 
				
			||||||
	BlockExploits         bool                 `json:"block_exploits" gorm:"column:block_exploits" filter:"block_exploits,boolean"`
 | 
						BlockExploits         bool                 `json:"block_exploits" gorm:"column:block_exploits" filter:"block_exploits,boolean"`
 | 
				
			||||||
@@ -101,9 +101,9 @@ func (m *Model) Expand(items []string) error {
 | 
				
			|||||||
	var err error
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Always expand the upstream
 | 
						// Always expand the upstream
 | 
				
			||||||
	if m.UpstreamID > 0 {
 | 
						if m.UpstreamID.Uint > 0 {
 | 
				
			||||||
		var u upstream.Model
 | 
							var u upstream.Model
 | 
				
			||||||
		u, err = upstream.GetByID(m.UpstreamID)
 | 
							u, err = upstream.GetByID(m.UpstreamID.Uint)
 | 
				
			||||||
		m.Upstream = &u
 | 
							m.Upstream = &u
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -113,9 +113,9 @@ func (m *Model) Expand(items []string) error {
 | 
				
			|||||||
		m.User = &usr
 | 
							m.User = &usr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if util.SliceContainsItem(items, "certificate") && m.CertificateID > 0 {
 | 
						if util.SliceContainsItem(items, "certificate") && m.CertificateID.Uint > 0 {
 | 
				
			||||||
		var cert certificate.Model
 | 
							var cert certificate.Model
 | 
				
			||||||
		cert, err = certificate.GetByID(m.CertificateID)
 | 
							cert, err = certificate.GetByID(m.CertificateID.Uint)
 | 
				
			||||||
		m.Certificate = &cert
 | 
							m.Certificate = &cert
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -125,9 +125,9 @@ func (m *Model) Expand(items []string) error {
 | 
				
			|||||||
		m.NginxTemplate = &templ
 | 
							m.NginxTemplate = &templ
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if util.SliceContainsItem(items, "upstream") && m.UpstreamID > 0 {
 | 
						if util.SliceContainsItem(items, "upstream") && m.UpstreamID.Uint > 0 {
 | 
				
			||||||
		var ups upstream.Model
 | 
							var ups upstream.Model
 | 
				
			||||||
		ups, err = upstream.GetByID(m.UpstreamID)
 | 
							ups, err = upstream.GetByID(m.UpstreamID.Uint)
 | 
				
			||||||
		m.Upstream = &ups
 | 
							m.Upstream = &ups
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -150,9 +150,9 @@ func (m *Model) GetTemplate() Template {
 | 
				
			|||||||
		ProxyPort:             m.ProxyPort,
 | 
							ProxyPort:             m.ProxyPort,
 | 
				
			||||||
		ListenInterface:       m.ListenInterface,
 | 
							ListenInterface:       m.ListenInterface,
 | 
				
			||||||
		DomainNames:           domainNames,
 | 
							DomainNames:           domainNames,
 | 
				
			||||||
		UpstreamID:            m.UpstreamID,
 | 
							UpstreamID:            m.UpstreamID.Uint,
 | 
				
			||||||
		CertificateID:         m.CertificateID,
 | 
							CertificateID:         m.CertificateID.Uint,
 | 
				
			||||||
		AccessListID:          m.AccessListID,
 | 
							AccessListID:          m.AccessListID.Uint,
 | 
				
			||||||
		SSLForced:             m.SSLForced,
 | 
							SSLForced:             m.SSLForced,
 | 
				
			||||||
		CachingEnabled:        m.CachingEnabled,
 | 
							CachingEnabled:        m.CachingEnabled,
 | 
				
			||||||
		BlockExploits:         m.BlockExploits,
 | 
							BlockExploits:         m.BlockExploits,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"npm/internal/entity"
 | 
						"npm/internal/entity"
 | 
				
			||||||
	"npm/internal/entity/certificate"
 | 
						"npm/internal/entity/certificate"
 | 
				
			||||||
	"npm/internal/entity/host"
 | 
						"npm/internal/entity/host"
 | 
				
			||||||
 | 
						"npm/internal/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -51,7 +52,7 @@ server {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
				Status:                 certificate.StatusProvided,
 | 
									Status:                 certificate.StatusProvided,
 | 
				
			||||||
				Type:                   certificate.TypeHTTP,
 | 
									Type:                   certificate.TypeHTTP,
 | 
				
			||||||
				CertificateAuthorityID: 99,
 | 
									CertificateAuthorityID: types.NullableDBUint{Uint: 99},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			want: want{
 | 
								want: want{
 | 
				
			||||||
				output: "\nserver {\n    include /etc/nginx/conf.d/npm/conf.d/acme-challenge.conf;\n    include /etc/nginx/conf.d/npm/conf.d/include/ssl-ciphers.conf;\n    ssl_certificate /npm-77/fullchain.pem;\n    ssl_certificate_key /npm-77/privkey.pem;\n}\n",
 | 
									output: "\nserver {\n    include /etc/nginx/conf.d/npm/conf.d/acme-challenge.conf;\n    include /etc/nginx/conf.d/npm/conf.d/include/ssl-ciphers.conf;\n    ssl_certificate /npm-77/fullchain.pem;\n    ssl_certificate_key /npm-77/privkey.pem;\n}\n",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										63
									
								
								backend/internal/types/db_nullable_int.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								backend/internal/types/db_nullable_int.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql/driver"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NullableDBInt works with database values that can be null or an integer value
 | 
				
			||||||
 | 
					type NullableDBInt struct {
 | 
				
			||||||
 | 
						Int int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Value encodes the type ready for the database
 | 
				
			||||||
 | 
					func (d NullableDBInt) Value() (driver.Value, error) {
 | 
				
			||||||
 | 
						if d.Int == 0 {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// According to current database/sql docs, the sql has four builtin functions that
 | 
				
			||||||
 | 
						// returns driver.Value, and the underlying types are `int64`, `float64`, `string` and `bool`
 | 
				
			||||||
 | 
						return driver.Value(int64(d.Int)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Scan takes data from the database and modifies it for Go Types
 | 
				
			||||||
 | 
					func (d *NullableDBInt) Scan(src interface{}) error {
 | 
				
			||||||
 | 
						var i int
 | 
				
			||||||
 | 
						switch v := src.(type) {
 | 
				
			||||||
 | 
						case int:
 | 
				
			||||||
 | 
							i = v
 | 
				
			||||||
 | 
						case int64:
 | 
				
			||||||
 | 
							i = int(v)
 | 
				
			||||||
 | 
						case float32:
 | 
				
			||||||
 | 
							i = int(v)
 | 
				
			||||||
 | 
						case float64:
 | 
				
			||||||
 | 
							i = int(v)
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							i, _ = strconv.Atoi(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.Int = i
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnmarshalJSON will unmarshal both database and post given values
 | 
				
			||||||
 | 
					func (d *NullableDBInt) UnmarshalJSON(data []byte) error {
 | 
				
			||||||
 | 
						// total_deploy_time: 10,
 | 
				
			||||||
 | 
						// total_deploy_time: null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var i int
 | 
				
			||||||
 | 
						if err := json.Unmarshal(data, &i); err != nil {
 | 
				
			||||||
 | 
							i = 0
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.Int = i
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MarshalJSON will marshal for output in api responses
 | 
				
			||||||
 | 
					func (d NullableDBInt) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						if d.Int == 0 {
 | 
				
			||||||
 | 
							return json.Marshal(nil)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return json.Marshal(d.Int)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										64
									
								
								backend/internal/types/db_nullable_uint.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								backend/internal/types/db_nullable_uint.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"database/sql/driver"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NullableDBUint works with database values that can be null or an integer value
 | 
				
			||||||
 | 
					type NullableDBUint struct {
 | 
				
			||||||
 | 
						Uint uint
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Value encodes the type ready for the database
 | 
				
			||||||
 | 
					func (d NullableDBUint) Value() (driver.Value, error) {
 | 
				
			||||||
 | 
						if d.Uint == 0 {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// According to current database/sql docs, the sql has four builtin functions that
 | 
				
			||||||
 | 
						// returns driver.Value, and the underlying types are `int64`, `float64`, `string` and `bool`
 | 
				
			||||||
 | 
						return driver.Value(int64(d.Uint)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Scan takes data from the database and modifies it for Go Types
 | 
				
			||||||
 | 
					func (d *NullableDBUint) Scan(src interface{}) error {
 | 
				
			||||||
 | 
						var i uint
 | 
				
			||||||
 | 
						switch v := src.(type) {
 | 
				
			||||||
 | 
						case int:
 | 
				
			||||||
 | 
							i = uint(v)
 | 
				
			||||||
 | 
						case int64:
 | 
				
			||||||
 | 
							i = uint(v)
 | 
				
			||||||
 | 
						case float32:
 | 
				
			||||||
 | 
							i = uint(v)
 | 
				
			||||||
 | 
						case float64:
 | 
				
			||||||
 | 
							i = uint(v)
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							a, _ := strconv.Atoi(v)
 | 
				
			||||||
 | 
							i = uint(a)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.Uint = i
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnmarshalJSON will unmarshal both database and post given values
 | 
				
			||||||
 | 
					func (d *NullableDBUint) UnmarshalJSON(data []byte) error {
 | 
				
			||||||
 | 
						// total_deploy_time: 10,
 | 
				
			||||||
 | 
						// total_deploy_time: null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var i uint
 | 
				
			||||||
 | 
						if err := json.Unmarshal(data, &i); err != nil {
 | 
				
			||||||
 | 
							i = 0
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.Uint = i
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MarshalJSON will marshal for output in api responses
 | 
				
			||||||
 | 
					func (d NullableDBUint) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						if d.Uint == 0 {
 | 
				
			||||||
 | 
							return json.Marshal(nil)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return json.Marshal(d.Uint)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -17,6 +17,8 @@ func (d NullableDBDate) Value() (driver.Value, error) {
 | 
				
			|||||||
	if d.Time == nil {
 | 
						if d.Time == nil {
 | 
				
			||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// According to current database/sql docs, the sql has four builtin functions that
 | 
				
			||||||
 | 
						// returns driver.Value, and the underlying types are `int64`, `float64`, `string` and `bool`
 | 
				
			||||||
	return driver.Value(d.Time.Unix()), nil
 | 
						return driver.Value(d.Time.Unix()), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								backend/internal/util/time.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								backend/internal/util/time.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UnixMilliToNiceFormat converts a millisecond to nice string
 | 
				
			||||||
 | 
					func UnixMilliToNiceFormat(milli int64) string {
 | 
				
			||||||
 | 
						t := time.Unix(0, milli*int64(time.Millisecond))
 | 
				
			||||||
 | 
						return t.Format("2006-01-02 15:04:05")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -12,27 +12,27 @@ import (
 | 
				
			|||||||
// ValidateHost will check if associated objects exist and other checks
 | 
					// ValidateHost will check if associated objects exist and other checks
 | 
				
			||||||
// will return  a nil error if things are OK
 | 
					// will return  a nil error if things are OK
 | 
				
			||||||
func ValidateHost(h host.Model) error {
 | 
					func ValidateHost(h host.Model) error {
 | 
				
			||||||
	if h.CertificateID > 0 {
 | 
						if h.CertificateID.Uint > 0 {
 | 
				
			||||||
		// Check certificate exists and is valid
 | 
							// Check certificate exists and is valid
 | 
				
			||||||
		// This will not determine if the certificate is Ready to use,
 | 
							// This will not determine if the certificate is Ready to use,
 | 
				
			||||||
		// as this validation only cares that the row exists.
 | 
							// as this validation only cares that the row exists.
 | 
				
			||||||
		if _, cErr := certificate.GetByID(h.CertificateID); cErr != nil {
 | 
							if _, cErr := certificate.GetByID(h.CertificateID.Uint); cErr != nil {
 | 
				
			||||||
			return eris.Wrapf(cErr, "Certificate #%d does not exist", h.CertificateID)
 | 
								return eris.Wrapf(cErr, "Certificate #%d does not exist", h.CertificateID.Uint)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if h.UpstreamID > 0 {
 | 
						if h.UpstreamID.Uint > 0 {
 | 
				
			||||||
		// Check upstream exists
 | 
							// Check upstream exists
 | 
				
			||||||
		if _, uErr := upstream.GetByID(h.UpstreamID); uErr != nil {
 | 
							if _, uErr := upstream.GetByID(h.UpstreamID.Uint); uErr != nil {
 | 
				
			||||||
			return eris.Wrapf(uErr, "Upstream #%d does not exist", h.UpstreamID)
 | 
								return eris.Wrapf(uErr, "Upstream #%d does not exist", h.UpstreamID.Uint)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Ensure either UpstreamID is set or appropriate proxy host params are set
 | 
						// Ensure either UpstreamID is set or appropriate proxy host params are set
 | 
				
			||||||
	if h.UpstreamID > 0 && (h.ProxyHost != "" || h.ProxyPort > 0) {
 | 
						if h.UpstreamID.Uint > 0 && (h.ProxyHost != "" || h.ProxyPort > 0) {
 | 
				
			||||||
		return eris.Errorf("Proxy Host or Port cannot be set when using an Upstream")
 | 
							return eris.Errorf("Proxy Host or Port cannot be set when using an Upstream")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if h.UpstreamID == 0 && (h.ProxyHost == "" || h.ProxyPort < 1) {
 | 
						if h.UpstreamID.Uint == 0 && (h.ProxyHost == "" || h.ProxyPort < 1) {
 | 
				
			||||||
		return eris.Errorf("Proxy Host and Port must be specified, unless using an Upstream")
 | 
							return eris.Errorf("Proxy Host and Port must be specified, unless using an Upstream")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user