mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2026-11-15 07:38:27 +00:00
Move away from dbmate, use PolySchema
This commit is contained in:
1 parent
52399c42f1
commit
2a14f3f84c
99 files changed
+1583
-1691
No files matched your search
+150
-139
@@ -1,4 +1,17 @@
|
||||
---
|
||||
version: "2"
|
||||
run:
|
||||
go: "1.24"
|
||||
# If set, we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the
|
||||
# implicit automatic updating of go.mod described above. Instead, it
|
||||
# fails when any changes to go.mod are needed. This setting is most
|
||||
# useful to check that go.mod does not need updates, such as in a
|
||||
# continuous integration and testing system.
|
||||
#
|
||||
# Allowed values: readonly|vendor|mod
|
||||
# Default: ""
|
||||
modules-download-mode: readonly
|
||||
linters:
|
||||
enable:
|
||||
# Prevents against memory leaks in production caused by not closing
|
||||
@@ -7,42 +20,19 @@ linters:
|
||||
# Detects cloned code. DRY is good programming practice. Can cause issues
|
||||
# with testing code where simplicity is preferred over duplication.
|
||||
# Disabled for test code.
|
||||
# - dupl
|
||||
# Detects unchecked errors in go programs. These unchecked errors can be
|
||||
# critical bugs in some cases.
|
||||
- errcheck
|
||||
# Simplifies go code.
|
||||
- gosimple
|
||||
# Controls Go package import order and makes it always deterministic.
|
||||
- gci
|
||||
# Reports suspicious constructs, maintained by goteam. e.g. Printf unused
|
||||
# params not caught at compile time.
|
||||
- govet
|
||||
# Detect security issues with gocode. Use of secrets in code or obsolete
|
||||
# security algorithms. It's imaged heuristic methods are used in finding
|
||||
# problems. If issues with rules are found particular rules can be disabled
|
||||
# as required. Could possibility cause issues with testing.
|
||||
# Disabled for test code.
|
||||
- gosec
|
||||
# Detect repeated strings that could be replaced by a constant
|
||||
- dupl
|
||||
- goconst
|
||||
# Misc linters missing from other projects. Grouped into 3 categories
|
||||
# diagnostics, style and performance
|
||||
- gocritic
|
||||
# Limits code cyclomatic complexity
|
||||
- gocyclo
|
||||
# Detects if code needs to be gofmt'd
|
||||
- gofmt
|
||||
# Detects unused go package imports
|
||||
- goimports
|
||||
# Detects style mistakes not correctness. Golint is meant to carry out the
|
||||
# stylistic conventions put forth in Effective Go and CodeReviewComments.
|
||||
# golint has false positives and false negatives and can be tweaked.
|
||||
- revive
|
||||
# Detects ineffectual assignments in code
|
||||
- ineffassign
|
||||
# Reports long lines
|
||||
# - lll
|
||||
# Detect security issues with gocode. Use of secrets in code or obsolete
|
||||
# security algorithms. It's imaged heuristic methods are used in finding
|
||||
# problems. If issues with rules are found particular rules can be disabled
|
||||
# as required. Could possibility cause issues with testing.
|
||||
# Disabled for test code.
|
||||
- gosec
|
||||
# Detect commonly misspelled english words in comments
|
||||
- misspell
|
||||
# Detect naked returns on non-trivial functions, and conform with
|
||||
@@ -50,117 +40,138 @@ linters:
|
||||
- nakedret
|
||||
# Detect slice allocations that can be preallocated
|
||||
- prealloc
|
||||
# Misc collection of static analysis tools
|
||||
- staticcheck
|
||||
# Detects unused struct fields
|
||||
# - structcheck
|
||||
# Parses and typechecks the code like the go compiler
|
||||
- typecheck
|
||||
# Detects unused constants, variables, functions and types
|
||||
- unused
|
||||
# Detects style mistakes not correctness. Golint is meant to carry out the
|
||||
# stylistic conventions put forth in Effective Go and CodeReviewComments.
|
||||
# golint has false positives and false negatives and can be tweaked.
|
||||
- revive
|
||||
# Remove unnecessary type conversions
|
||||
- unconvert
|
||||
# Remove unnecessary(unused) function parameters
|
||||
- unparam
|
||||
linters-settings:
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- fmt.Fprint
|
||||
- fmt.Fprintf
|
||||
gci:
|
||||
sections:
|
||||
- standard # Standard section: captures all standard packages.
|
||||
- localmodule # Local module section: contains all local packages.
|
||||
# - prefix(gogs.jc21.com) # Prefixed gerrit.lan packages (jumgo).
|
||||
- default # Everything else (github.com, golang.org, etc).
|
||||
- blank # Blank section: contains all blank imports.
|
||||
custom-order: true
|
||||
goconst:
|
||||
# minimal length of string constant
|
||||
# default: 3
|
||||
min-len: 2
|
||||
# minimum number of occurrences of string constant
|
||||
# default: 3
|
||||
min-occurences: 2
|
||||
revive:
|
||||
enable-all-rules: true
|
||||
settings:
|
||||
# Detects unchecked errors in go programs. These unchecked errors can be
|
||||
# critical bugs in some cases.
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- fmt.Fprint
|
||||
- fmt.Fprintf
|
||||
# Detect repeated strings that could be replaced by a constant
|
||||
goconst:
|
||||
# minimal length of string constant
|
||||
# default: 3
|
||||
min-len: 2
|
||||
gosec:
|
||||
excludes:
|
||||
- G201 # Allow formatted string for queries
|
||||
revive:
|
||||
enable-all-rules: true
|
||||
rules:
|
||||
# handled by goconst
|
||||
- name: add-constant
|
||||
disabled: true
|
||||
# cant limit this arbitrarily
|
||||
- name: argument-limit
|
||||
disabled: true
|
||||
# handled by gocyclo
|
||||
- name: cognitive-complexity
|
||||
disabled: true
|
||||
# false positive for Exported vs non-exported functions of the same name
|
||||
- name: confusing-naming
|
||||
disabled: true
|
||||
# false positives for "" - which is the nil value of a string (also 0)
|
||||
- name: confusing-results
|
||||
disabled: true
|
||||
# handled by gocyclo
|
||||
- name: cyclomatic
|
||||
disabled: true
|
||||
# some cli programs want to exit on error
|
||||
- name: deep-exit
|
||||
disabled: true
|
||||
# have comments on exported functions but not on vars/types/constants
|
||||
- name: exported
|
||||
arguments:
|
||||
- disableChecksOnConstants
|
||||
- disableChecksOnTypes
|
||||
- disableChecksOnVariables
|
||||
# false positives on bool params
|
||||
- name: flag-parameter
|
||||
disabled: true
|
||||
# extreme verticalization can happen
|
||||
- name: function-length
|
||||
disabled: true
|
||||
# return as many as you like
|
||||
- name: function-result-limit
|
||||
disabled: true
|
||||
# can false positive for non-getters
|
||||
- name: get-return
|
||||
disabled: true
|
||||
# only allows lowercase names
|
||||
- name: import-alias-naming
|
||||
disabled: true
|
||||
# handled by lll
|
||||
- name: line-length-limit
|
||||
disabled: true
|
||||
# handled by gocyclo
|
||||
- name: max-control-nesting
|
||||
disabled: true
|
||||
# don't want to arbitrarily limit this
|
||||
# many places have specific model.go files to contain all structs
|
||||
- name: max-public-structs
|
||||
disabled: true
|
||||
# disable package-comments
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
# allow ignoring assertion result
|
||||
- name: unchecked-type-assertion
|
||||
arguments:
|
||||
- acceptIgnoredAssertionResult: true
|
||||
# this is handled by errcheck
|
||||
- name: unhandled-error
|
||||
disabled: true
|
||||
- name: enforce-switch-style
|
||||
disabled: true
|
||||
- name: package-naming
|
||||
disabled: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
rules:
|
||||
- name: unchecked-type-assertion
|
||||
disabled: true
|
||||
# handled by goconst
|
||||
- name: add-constant
|
||||
disabled: true
|
||||
# cant limit this arbitrarily
|
||||
- name: argument-limit
|
||||
disabled: true
|
||||
# handled by gocyclo
|
||||
- name: cognitive-complexity
|
||||
disabled: true
|
||||
# false positive for Exported vs non-exported functions of the same name
|
||||
- name: confusing-naming
|
||||
disabled: true
|
||||
# false positives for "" - which is the nil value of a string (also 0)
|
||||
- name: confusing-results
|
||||
disabled: true
|
||||
# handled by gocyclo
|
||||
- name: cyclomatic
|
||||
disabled: true
|
||||
# have comments on exported functions but not on vars/types/constants
|
||||
- name: exported
|
||||
arguments:
|
||||
- "disableChecksOnConstants"
|
||||
- "disableChecksOnTypes"
|
||||
- "disableChecksOnVariables"
|
||||
# false positives on bool params
|
||||
- name: flag-parameter
|
||||
disabled: true
|
||||
# extreme verticalization can happen
|
||||
- name: function-length
|
||||
disabled: true
|
||||
# can false positive for non-getters
|
||||
- name: get-return
|
||||
disabled: true
|
||||
# only allows lowercase names
|
||||
- name: import-alias-naming
|
||||
disabled: true
|
||||
# handled by lll
|
||||
- name: line-length-limit
|
||||
disabled: true
|
||||
# don't want to arbitrarily limit this
|
||||
# many places have specific model.go files to contain all structs
|
||||
- name: max-public-structs
|
||||
disabled: true
|
||||
# disable package-comments
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
# this is handled by errcheck
|
||||
- name: unhandled-error
|
||||
disabled: true
|
||||
- name: function-result-limit
|
||||
disabled: true
|
||||
issues:
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default
|
||||
# is 3. We have chosen an arbitrary value that works based on practical usage.
|
||||
max-same: 20
|
||||
# See cmdline flag documentation for more info about default excludes
|
||||
# --exclude-use-default. Nothing is excluded by default
|
||||
exclude-use-default: false
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
# TODO: Add examples why this is good
|
||||
- path: _test\.go
|
||||
linters:
|
||||
# Tests should be simple? Add example why this is good?
|
||||
- gocyclo
|
||||
# Error checking adds verbosity and complexity for minimal value
|
||||
- errcheck
|
||||
# Table test encourage duplication in defining the table tests.
|
||||
- dupl
|
||||
# Hard coded example tokens, SQL injection and other bad practices may
|
||||
# want to be tested
|
||||
- gosec
|
||||
# Test data can long
|
||||
# - lll
|
||||
run:
|
||||
go: '1.23'
|
||||
- linters:
|
||||
# Table test encourage duplication in defining the table tests.
|
||||
- dupl
|
||||
# Test fixtures read better as literals than as constants
|
||||
- goconst
|
||||
# Error checking adds verbosity and complexity for minimal value
|
||||
- errcheck
|
||||
# Tests should be simple? Add example why this is good?
|
||||
- gocyclo
|
||||
# Hard coded example tokens, SQL injection and other bad practices may
|
||||
# want to be tested
|
||||
- gosec
|
||||
# Test data can long
|
||||
- lll
|
||||
path: _test\.go
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
# Controls Go package import order and makes it always deterministic.
|
||||
- gci
|
||||
# Detects if code needs to be gofmt'd
|
||||
- gofmt
|
||||
settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard # Standard section: captures all standard packages
|
||||
- localmodule # Local module section contains all local packages
|
||||
- prefix(gerrit.lan) # Prefixed gerrit.lan packages (jumgo)
|
||||
- default # Everything else (github.com, golang.org, etc)
|
||||
- blank # Blank section: contains all blank imports
|
||||
custom-order: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
@@ -12,6 +12,11 @@ var APIDocFiles embed.FS
|
||||
//go:embed assets
|
||||
var Assets embed.FS
|
||||
|
||||
// MigrationFiles is for database schema migrations
|
||||
//
|
||||
//go:embed migrations
|
||||
var MigrationFiles embed.FS
|
||||
|
||||
// NginxFiles hold nginx config templates
|
||||
//
|
||||
//go:embed nginx
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
# The application's initial schema.
|
||||
#
|
||||
# Columns are nullable unless marked otherwise. Two columns use a bounded
|
||||
# string rather than text because MySQL can't make a TEXT column a primary
|
||||
# key (capability.name) or give it a literal default (auth.type). They're
|
||||
# still TEXT on SQLite.
|
||||
operations:
|
||||
- createTable:
|
||||
name: access_list
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: meta, type: text }
|
||||
|
||||
- createTable:
|
||||
name: audit_log
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: object_type, type: text }
|
||||
- { name: object_id, type: bigint }
|
||||
- { name: action, type: text }
|
||||
- { name: meta, type: text }
|
||||
|
||||
- createTable:
|
||||
name: auth
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: type, type: string, length: 191, default: local }
|
||||
- { name: identity, type: text }
|
||||
- { name: secret, type: text }
|
||||
|
||||
- createTable:
|
||||
name: capability
|
||||
columns:
|
||||
- { name: name, type: string, length: 191, primaryKey: true }
|
||||
|
||||
- createTable:
|
||||
name: certificate
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: type, type: text }
|
||||
- { name: certificate_authority_id, type: bigint }
|
||||
- { name: dns_provider_id, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: domain_names, type: text }
|
||||
- { name: expires_on, type: bigint }
|
||||
- { name: status, type: text }
|
||||
- { name: error_message, type: text }
|
||||
- { name: meta, type: text }
|
||||
- { name: is_ecc, type: boolean }
|
||||
|
||||
- createTable:
|
||||
name: certificate_authority
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: acmesh_server, type: text }
|
||||
- { name: ca_bundle, type: text }
|
||||
- { name: max_domains, type: bigint }
|
||||
- { name: is_wildcard_supported, type: boolean }
|
||||
- { name: is_readonly, type: boolean }
|
||||
|
||||
- createTable:
|
||||
name: dns_provider
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: acmesh_name, type: text }
|
||||
- { name: dns_sleep, type: bigint }
|
||||
- { name: meta, type: text }
|
||||
|
||||
- createTable:
|
||||
name: host
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: type, type: text }
|
||||
- { name: nginx_template_id, type: bigint }
|
||||
- { name: listen_interface, type: text }
|
||||
- { name: domain_names, type: text }
|
||||
- { name: upstream_id, type: bigint }
|
||||
- { name: proxy_scheme, type: text }
|
||||
- { name: proxy_host, type: text }
|
||||
- { name: proxy_port, type: bigint }
|
||||
- { name: certificate_id, type: bigint }
|
||||
- { name: access_list_id, type: bigint }
|
||||
- { name: ssl_forced, type: boolean }
|
||||
- { name: caching_enabled, type: boolean }
|
||||
- { name: block_exploits, type: boolean }
|
||||
- { name: allow_websocket_upgrade, type: boolean }
|
||||
- { name: http2_support, type: boolean }
|
||||
- { name: hsts_enabled, type: boolean }
|
||||
- { name: hsts_subdomains, type: boolean }
|
||||
- { name: paths, type: text }
|
||||
- { name: advanced_config, type: text }
|
||||
- { name: status, type: text }
|
||||
- { name: error_message, type: text }
|
||||
- { name: is_disabled, type: boolean }
|
||||
|
||||
- createTable:
|
||||
name: jwt_keys
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: public_key, type: text }
|
||||
- { name: private_key, type: text }
|
||||
|
||||
- createTable:
|
||||
name: nginx_template
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: type, type: text }
|
||||
- { name: template, type: text }
|
||||
|
||||
- createTable:
|
||||
name: setting
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: description, type: text }
|
||||
- { name: value, type: json }
|
||||
|
||||
- createTable:
|
||||
name: stream
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: expires_on, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: provider, type: text }
|
||||
- { name: name, type: text }
|
||||
- { name: domain_names, type: text }
|
||||
- { name: meta, type: text }
|
||||
|
||||
- createTable:
|
||||
name: upstream
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: nginx_template_id, type: bigint }
|
||||
- { name: ip_hash, type: boolean }
|
||||
- { name: ntlm, type: boolean }
|
||||
- { name: keepalive, type: bigint }
|
||||
- { name: keepalive_requests, type: bigint }
|
||||
- { name: keepalive_time, type: text }
|
||||
- { name: keepalive_timeout, type: text }
|
||||
- { name: advanced_config, type: text }
|
||||
- { name: status, type: text }
|
||||
- { name: error_message, type: text }
|
||||
|
||||
- createTable:
|
||||
name: upstream_server
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: upstream_id, type: bigint }
|
||||
- { name: server, type: text }
|
||||
- { name: weight, type: bigint }
|
||||
- { name: max_conns, type: bigint }
|
||||
- { name: max_fails, type: bigint }
|
||||
- { name: fail_timeout, type: bigint }
|
||||
- { name: is_backup, type: boolean }
|
||||
|
||||
- createTable:
|
||||
name: user
|
||||
columns:
|
||||
- { name: id, type: bigint, primaryKey: true, autoIncrement: true }
|
||||
- { name: created_at, type: bigint }
|
||||
- { name: updated_at, type: bigint }
|
||||
- { name: is_deleted, type: bigint }
|
||||
- { name: name, type: text }
|
||||
- { name: email, type: text }
|
||||
- { name: is_disabled, type: boolean }
|
||||
- { name: is_system, type: boolean }
|
||||
|
||||
- createTable:
|
||||
name: user_has_capability
|
||||
columns:
|
||||
- { name: user_id, type: bigint }
|
||||
- { name: capability_name, type: text }
|
||||
@@ -0,0 +1,253 @@
|
||||
# Seed data for a new database.
|
||||
#
|
||||
# created_at/updated_at hold unix epoch milliseconds. polyschema always sends
|
||||
# insert values as bind parameters, so an SQL expression for "now" can't be
|
||||
# used as a value. The rows are inserted without them, and the sql operations
|
||||
# at the end fill them in using each engine's own clock.
|
||||
operations:
|
||||
# Capabilities
|
||||
- insert:
|
||||
table: capability
|
||||
rows:
|
||||
- { name: 'full-admin' }
|
||||
- { name: 'access-lists.view' }
|
||||
- { name: 'access-lists.manage' }
|
||||
- { name: 'audit-log.view' }
|
||||
- { name: 'certificates.view' }
|
||||
- { name: 'certificates.manage' }
|
||||
- { name: 'certificate-authorities.view' }
|
||||
- { name: 'certificate-authorities.manage' }
|
||||
- { name: 'dns-providers.view' }
|
||||
- { name: 'dns-providers.manage' }
|
||||
- { name: 'hosts.view' }
|
||||
- { name: 'hosts.manage' }
|
||||
- { name: 'nginx-templates.view' }
|
||||
- { name: 'nginx-templates.manage' }
|
||||
- { name: 'settings.manage' }
|
||||
- { name: 'streams.view' }
|
||||
- { name: 'streams.manage' }
|
||||
- { name: 'users.manage' }
|
||||
# Default settings
|
||||
- insert:
|
||||
table: setting
|
||||
rows:
|
||||
- { is_deleted: 0, name: 'default-site', description: 'What to show users who hit your Nginx server by default', value: '"welcome"' }
|
||||
- { is_deleted: 0, name: 'auth-methods', description: 'Which methods are enabled for authentication', value: '["local"]' }
|
||||
- { is_deleted: 0, name: 'oauth-auth', description: 'Configuration for OAuth authentication', value: '{}' }
|
||||
- { is_deleted: 0, name: 'ldap-auth', description: 'Configuration for LDAP authentication', value: '{"host": "", "dn": "", "sync_by": "uid"}' }
|
||||
# Built-in certificate authorities
|
||||
- insert:
|
||||
table: certificate_authority
|
||||
rows:
|
||||
- { is_deleted: 0, name: 'ZeroSSL', acmesh_server: 'zerossl', max_domains: 10, is_wildcard_supported: true, is_readonly: true }
|
||||
- { is_deleted: 0, name: 'Let''s Encrypt', acmesh_server: 'https://acme-v02.api.letsencrypt.org/directory', max_domains: 10, is_wildcard_supported: true, is_readonly: true }
|
||||
- { is_deleted: 0, name: 'Buypass Go SSL', acmesh_server: 'https://api.buypass.com/acme/directory', max_domains: 5, is_wildcard_supported: false, is_readonly: true }
|
||||
- { is_deleted: 0, name: 'SSL.com', acmesh_server: 'ssl.com', max_domains: 10, is_wildcard_supported: false, is_readonly: true }
|
||||
- { is_deleted: 0, name: 'Let''s Encrypt (Testing)', acmesh_server: 'https://acme-staging-v02.api.letsencrypt.org/directory', max_domains: 10, is_wildcard_supported: true, is_readonly: true }
|
||||
- { is_deleted: 0, name: 'Buypass Go SSL (Testing)', acmesh_server: 'https://api.test4.buypass.no/acme/directory', max_domains: 5, is_wildcard_supported: false, is_readonly: true }
|
||||
# The system user (id 1, owns the built-in nginx templates)
|
||||
- insert:
|
||||
table: user
|
||||
rows:
|
||||
- { is_deleted: 0, name: 'System', email: 'system@localhost', is_disabled: false, is_system: true }
|
||||
# Built-in nginx templates
|
||||
- insert:
|
||||
table: nginx_template
|
||||
rows:
|
||||
- is_deleted: 0
|
||||
user_id: 1
|
||||
name: 'Default Proxy Template'
|
||||
type: 'proxy'
|
||||
template: |
|
||||
# ------------------------------------------------------------
|
||||
{{#each Host.DomainNames}}
|
||||
# {{this}}
|
||||
{{/each}}
|
||||
# ------------------------------------------------------------
|
||||
|
||||
server {
|
||||
{{#if Config.Ipv4}}
|
||||
listen 80;
|
||||
{{/if}}
|
||||
{{#if Config.Ipv6}}
|
||||
listen [::]:80;
|
||||
{{/if}}
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
{{#if Config.Ipv4}}
|
||||
listen 443 ssl {{#if Host.HTTP2Support}}http2{{/if}};
|
||||
{{/if}}
|
||||
{{#if Config.Ipv6}}
|
||||
listen [::]:443 ssl {{#if Host.HTTP2Support}}http2{{/if}};
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
server_name {{#each Host.DomainNames}}{{this}} {{/each}};
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
include conf.d/include/ssl-ciphers.conf;
|
||||
{{#if Certificate.IsAcme}}
|
||||
ssl_certificate {{Certificate.Folder}}/fullchain.pem;
|
||||
ssl_certificate_key {{Certificate.Folder}}/privkey.pem;
|
||||
{{else}}
|
||||
# Custom SSL
|
||||
ssl_certificate /data/custom_ssl/npm-{{Certicicate.ID}}/fullchain.pem;
|
||||
ssl_certificate_key /data/custom_ssl/npm-{{Certificate.ID}}/privkey.pem;
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.CachingEnabled}}
|
||||
include conf.d/include/assets.conf;
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.BlockExploits}}
|
||||
include conf.d/include/block-exploits.conf;
|
||||
{{/if}}
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
{{#if Host.SSLForced}}
|
||||
{{#if Host.HSTSEnabled}}
|
||||
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
|
||||
add_header Strict-Transport-Security "max-age=63072000;{{#if Host.HSTSSubdomains}} includeSubDomains;{{/if}} preload" always;
|
||||
{{/if}}
|
||||
# Force SSL
|
||||
include conf.d/include/force-ssl.conf;
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.AllowWebsocketUpgrade}}
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
proxy_http_version 1.1;
|
||||
{{/if}}
|
||||
|
||||
access_log /data/logs/host-{{Host.ID}}_access.log proxy;
|
||||
error_log /data/logs/host-{{Host.ID}}_error.log warn;
|
||||
|
||||
{{Host.AdvancedConfig}}
|
||||
|
||||
# locations ?
|
||||
|
||||
# default location:
|
||||
location / {
|
||||
{{#if Host.AccessListID}}
|
||||
# Authorization
|
||||
auth_basic "Authorization required";
|
||||
auth_basic_user_file /data/access/{{Host.AccessListID}};
|
||||
# access_list.passauth ? todo
|
||||
{{/if}}
|
||||
|
||||
# Access Rules ? todo
|
||||
|
||||
# Access checks must...? todo
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
{{#if Host.SSLForced}}
|
||||
{{#if Host.HSTSEnabled}}
|
||||
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
|
||||
add_header Strict-Transport-Security "max-age=63072000;{{#if Host.HSTSSubdomains}} includeSubDomains;{{/if}} preload" always;
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.AllowWebsocketUpgrade}}
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
{{/if}}
|
||||
|
||||
# Proxy!
|
||||
add_header X-Served-By $host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
{{#if Upstream.ID}}
|
||||
# upstream
|
||||
proxy_pass {{Host.ProxyScheme}}://npm_upstream_{{Upstream.ID}};
|
||||
{{else}}
|
||||
# proxy a single host
|
||||
proxy_pass {{Host.ProxyScheme}}://{{Host.ProxyHost}}:{{Host.ProxyPort}};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
# Legacy Custom Configuration
|
||||
include /data/nginx/custom/server_proxy[.]conf;
|
||||
}
|
||||
- is_deleted: 0
|
||||
user_id: 1
|
||||
name: 'Default Redirect Template'
|
||||
type: 'redirect'
|
||||
template: '# this is a redirect template'
|
||||
- is_deleted: 0
|
||||
user_id: 1
|
||||
name: 'Default Dead Template'
|
||||
type: 'dead'
|
||||
template: '# this is a dead template'
|
||||
- is_deleted: 0
|
||||
user_id: 1
|
||||
name: 'Default Stream Template'
|
||||
type: 'stream'
|
||||
template: '# this is a stream template'
|
||||
- is_deleted: 0
|
||||
user_id: 1
|
||||
name: 'Default Upstream Template'
|
||||
type: 'upstream'
|
||||
template: |
|
||||
# ------------------------------------------------------------
|
||||
# Upstream {{Upstream.ID}}: {{Upstream.Name}}
|
||||
# ------------------------------------------------------------
|
||||
|
||||
upstream npm_upstream_{{Upstream.ID}} {
|
||||
|
||||
{{#if Upstream.IPHash~}}
|
||||
ip_hash;
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.NTLM~}}
|
||||
ntlm;
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.Keepalive~}}
|
||||
keepalive {{Upstream.Keepalive}};
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.KeepaliveRequests~}}
|
||||
keepalive_requests {{Upstream.KeepaliveRequests}};
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.KeepaliveTime~}}
|
||||
keepalive_time {{Upstream.KeepaliveTime}};
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.KeepaliveTimeout~}}
|
||||
keepalive_timeout {{Upstream.KeepaliveTimeout}};
|
||||
{{~/if}}
|
||||
|
||||
{{Upstream.AdvancedConfig}}
|
||||
|
||||
{{#each Upstream.Servers~}}
|
||||
{{#unless IsDeleted~}}
|
||||
server {{Server}} {{#if Weight}}weight={{Weight}} {{/if}}{{#if MaxConns}}max_conns={{MaxConns}} {{/if}}{{#if MaxFails}}max_fails={{MaxFails}} {{/if}}{{#if FailTimeout}}fail_timeout={{FailTimeout}} {{/if}}{{#if Backup}}backup{{/if}};
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
# Timestamps for everything above
|
||||
- sql:
|
||||
postgres: 'UPDATE "setting" SET created_at = (extract(epoch from clock_timestamp()) * 1000)::bigint, updated_at = (extract(epoch from clock_timestamp()) * 1000)::bigint'
|
||||
mysql: 'UPDATE `setting` SET created_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED), updated_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED)'
|
||||
sqlite: 'UPDATE "setting" SET created_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER), updated_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER)'
|
||||
- sql:
|
||||
postgres: 'UPDATE "certificate_authority" SET created_at = (extract(epoch from clock_timestamp()) * 1000)::bigint, updated_at = (extract(epoch from clock_timestamp()) * 1000)::bigint'
|
||||
mysql: 'UPDATE `certificate_authority` SET created_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED), updated_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED)'
|
||||
sqlite: 'UPDATE "certificate_authority" SET created_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER), updated_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER)'
|
||||
- sql:
|
||||
postgres: 'UPDATE "user" SET created_at = (extract(epoch from clock_timestamp()) * 1000)::bigint, updated_at = (extract(epoch from clock_timestamp()) * 1000)::bigint'
|
||||
mysql: 'UPDATE `user` SET created_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED), updated_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED)'
|
||||
sqlite: 'UPDATE "user" SET created_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER), updated_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER)'
|
||||
- sql:
|
||||
postgres: 'UPDATE "nginx_template" SET created_at = (extract(epoch from clock_timestamp()) * 1000)::bigint, updated_at = (extract(epoch from clock_timestamp()) * 1000)::bigint'
|
||||
mysql: 'UPDATE `nginx_template` SET created_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED), updated_at = CAST(UNIX_TIMESTAMP(NOW(3)) * 1000 AS UNSIGNED)'
|
||||
sqlite: 'UPDATE "nginx_template" SET created_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER), updated_at = CAST((julianday(''now'') - 2440587.5) * 86400000 AS INTEGER)'
|
||||
+13
-12
@@ -1,6 +1,6 @@
|
||||
module npm
|
||||
|
||||
go 1.26.0
|
||||
go 1.27.1
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
@@ -12,11 +12,11 @@ require (
|
||||
github.com/go-chi/chi/v5 v5.3.2
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-chi/jwtauth/v5 v5.4.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.7
|
||||
github.com/go-ldap/ldap/v3 v3.4.14
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/jc21/go-sse v1.7.0
|
||||
github.com/jc21/jsref v0.0.0-20250501111625-0ce4620b7d96
|
||||
github.com/jc21/polyschema v0.0.0-20260925050705-5d30b3cbbc99
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/qri-io/jsonschema v0.2.1
|
||||
github.com/rotisserie/eris v0.5.4
|
||||
@@ -34,19 +34,19 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.1.1 // indirect
|
||||
github.com/alexflint/go-scalar v1.2.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.23.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.10.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.10.0 // indirect
|
||||
github.com/jackc/pgx/v5 v5.11.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
@@ -60,21 +60,22 @@ require (
|
||||
github.com/lestrrat-go/structinfo v0.0.0-20210312050401-7f8bd69d6acb // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.28 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/qri-io/jsonpointer v0.1.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/sync v0.23.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
modernc.org/libc v1.61.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.28.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.74.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.55.0 // indirect
|
||||
)
|
||||
|
||||
replace modernc.org/sqlite => gitlab.com/jc21com/sqlite v1.22.2-0.20230527022643-b56cedb3bc85
|
||||
+50
-38
@@ -1,5 +1,5 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw=
|
||||
github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
@@ -23,8 +23,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/go-sqlite v1.23.0 h1:FyhIq4jqmgphQAUlY79zPldYGwISEZikaDfhiGWkkaI=
|
||||
github.com/glebarez/go-sqlite v1.23.0/go.mod h1:IIYrOH3L0rHY3jb4IXOHoWdklNajSGUN2eJcvK8WrnI=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
|
||||
@@ -35,12 +35,10 @@ github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/jwtauth/v5 v5.4.0 h1:Ieh0xMJsFvqylqJ02/mQHKzbbKO9DYNBh4DPKCwTwYI=
|
||||
github.com/go-chi/jwtauth/v5 v5.4.0/go.mod h1:w6yjqUUXz1b8+oiJel64Sz1KJwduQM6qUA5QNzO5+bQ=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.7 h1:PdT4jVPbRb4R+0Ey2R0yJOdctVf4Whiq1Qi4necaZdg=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.7/go.mod h1:3ouXglTuPrKF5+7cQyVGfvAXTU4vLMaYh9+EPl03uog=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-sql-driver/mysql v1.10.1 h1:arlSnNLq6a5yxGxV7qg9lF4j0C+KwD6NbQyKr9QL6ME=
|
||||
github.com/go-sql-driver/mysql v1.10.1/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
@@ -49,24 +47,28 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
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-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
|
||||
github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/pgx/v5 v5.11.0 h1:IzBBtyK9AHqf98cctWFifYSci2hgQR/cd56wB4p+ogg=
|
||||
github.com/jackc/pgx/v5 v5.11.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jc21/go-sse v1.7.0 h1:Lavb7FGssS2UdJMK1P3r4rQJgg2JZx7BINi8pd/QzKg=
|
||||
github.com/jc21/go-sse v1.7.0/go.mod h1:dbA0LtDgvSEhlAXz+meN1KAcdvcCLiF0aVdhjhZjbGI=
|
||||
github.com/jc21/jsref v0.0.0-20250501111625-0ce4620b7d96 h1:LzBXyNdALQHC7DUy8PE7IFTihmnSRop4Ps2cBIr/WP8=
|
||||
github.com/jc21/jsref v0.0.0-20250501111625-0ce4620b7d96/go.mod h1:Vno1sw0yqqImXnYy3q6ueIG+h+Vwwfbkfw9DTHCrHfY=
|
||||
github.com/jc21/polyschema v0.0.0-20260925050705-5d30b3cbbc99 h1:HOjJaUJjZehxSMoLxhg8sMnTkk/5ncFy+5gYnijZg60=
|
||||
github.com/jc21/polyschema v0.0.0-20260925050705-5d30b3cbbc99/go.mod h1:H8UH9o8FM1uvKiA8kd9daeeKLC5x5xxqiPtyIYoxFns=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
@@ -86,8 +88,11 @@ github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
|
||||
github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
|
||||
@@ -111,12 +116,12 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
|
||||
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
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/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
@@ -131,6 +136,8 @@ github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97W
|
||||
github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI=
|
||||
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/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g=
|
||||
github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
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/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||
@@ -165,8 +172,6 @@ go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
|
||||
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
@@ -184,9 +189,12 @@ golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
|
||||
gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
|
||||
@@ -205,25 +213,29 @@ gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
|
||||
gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
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/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
|
||||
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
|
||||
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/libc v1.61.0 h1:eGFcvWpqlnoGwzZeZe3PWJkkKbM/3SUGyk1DVZQ0TpE=
|
||||
modernc.org/libc v1.61.0/go.mod h1:DvxVX89wtGTu+r72MLGhygpfi3aUGgZRdAYGCAVVud0=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc=
|
||||
modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
|
||||
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
|
||||
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ=
|
||||
modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
@@ -2,32 +2,17 @@ package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/accesslist"
|
||||
)
|
||||
|
||||
// GetAccessLists will return a list of Access Lists
|
||||
// Route: GET /access-lists
|
||||
func GetAccessLists() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
items, err := accesslist.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, items)
|
||||
}
|
||||
}
|
||||
return listHandler(accesslist.List)
|
||||
}
|
||||
|
||||
// GetAccessList will return a single access list
|
||||
@@ -54,25 +39,7 @@ func GetAccessList() func(http.ResponseWriter, *http.Request) {
|
||||
// Route: POST /access-lists
|
||||
func CreateAccessList() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newItem accesslist.Model
|
||||
err := json.Unmarshal(bodyBytes, &newItem)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
||||
newItem.UserID = userID
|
||||
|
||||
if err = newItem.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Access List: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newItem)
|
||||
createUserOwned(w, r, &accesslist.Model{}, "Access List")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ func RefreshToken() func(http.ResponseWriter, *http.Request) {
|
||||
// Route: POST /auth/sse
|
||||
func NewSSEToken() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID := r.Context().Value(c.UserIDCtxKey).(uint)
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
||||
|
||||
// Find user
|
||||
userObj, userErr := user.GetByID(userID)
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// userOwned is an entity that belongs to the user that created it
|
||||
type userOwned interface {
|
||||
SetUserID(userID uint)
|
||||
Save() error
|
||||
}
|
||||
|
||||
// createUserOwned decodes the request body into item, assigns it to the
|
||||
// requesting user and saves it. label is used in the error message.
|
||||
func createUserOwned(w http.ResponseWriter, r *http.Request, item userOwned, label string) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
if err := json.Unmarshal(bodyBytes, item); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
||||
item.SetUserID(userID)
|
||||
|
||||
if err := item.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save %s: %s", label, err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
}
|
||||
|
||||
// getNginxConfig returns a handler that loads an entity by the ID in the
|
||||
// paramName url param and responds with its nginx config from disk, as
|
||||
// plain text when format is "text" or JSON otherwise
|
||||
func getNginxConfig[T any](
|
||||
format string,
|
||||
paramName string,
|
||||
getByID func(uint) (T, error),
|
||||
getContent func(T) (string, error),
|
||||
) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := getURLParamInt(r, paramName)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := getByID(id)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
// Get the config from disk
|
||||
content, nErr := getContent(item)
|
||||
if nErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, nErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
if format == "text" {
|
||||
h.ResultResponseText(w, http.StatusOK, content)
|
||||
return
|
||||
}
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, content)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getByIDHandler returns a handler that responds with the entity loaded by
|
||||
// the ID in the paramName url param
|
||||
func getByIDHandler[T any](paramName string, getByID func(uint) (T, error)) func(http.ResponseWriter, *http.Request) {
|
||||
return withEntity(paramName, getByID, func(w http.ResponseWriter, r *http.Request, item *T) {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
})
|
||||
}
|
||||
|
||||
// updateByIDHandler returns a handler that loads the entity by the ID in the
|
||||
// paramName url param, decodes the request body over it and saves it
|
||||
func updateByIDHandler[T any](
|
||||
paramName string,
|
||||
getByID func(uint) (T, error),
|
||||
save func(*T) error,
|
||||
) func(http.ResponseWriter, *http.Request) {
|
||||
return withEntity(paramName, getByID, func(w http.ResponseWriter, r *http.Request, item *T) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
if err := json.Unmarshal(bodyBytes, item); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := save(item); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
})
|
||||
}
|
||||
|
||||
// deleteByIDHandler returns a handler that loads the entity by the ID in the
|
||||
// paramName url param, deletes it and responds with the result of del
|
||||
func deleteByIDHandler[T, R any](
|
||||
paramName string,
|
||||
getByID func(uint) (T, error),
|
||||
del func(*T) R,
|
||||
) func(http.ResponseWriter, *http.Request) {
|
||||
return withEntity(paramName, getByID, func(w http.ResponseWriter, r *http.Request, item *T) {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, del(item))
|
||||
})
|
||||
}
|
||||
|
||||
// withEntity returns a handler that loads the entity by the ID in the
|
||||
// paramName url param and passes it to fn, responding with not found or
|
||||
// bad request when it can't be loaded
|
||||
func withEntity[T any](
|
||||
paramName string,
|
||||
getByID func(uint) (T, error),
|
||||
fn func(http.ResponseWriter, *http.Request, *T),
|
||||
) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := getURLParamInt(r, paramName)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := getByID(id)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
fn(w, r, &item)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// listHandler returns a handler that responds with a page of entities
|
||||
// matching the request's pagination and filters
|
||||
func listHandler(list func(model.PageInfo, []model.Filter) (entity.ListResponse, error)) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
items, err := list(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, items)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,144 +1,44 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/dnsproviders"
|
||||
"npm/internal/entity/dnsprovider"
|
||||
"npm/internal/errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GetDNSProviders will return a list of DNS Providers
|
||||
// Route: GET /dns-providers
|
||||
func GetDNSProviders() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
items, err := dnsprovider.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, items)
|
||||
}
|
||||
}
|
||||
return listHandler(dnsprovider.List)
|
||||
}
|
||||
|
||||
// GetDNSProvider will return a single DNS Provider
|
||||
// Route: GET /dns-providers/{providerID}
|
||||
func GetDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var providerID uint
|
||||
if providerID, err = getURLParamInt(r, "providerID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := dnsprovider.GetByID(providerID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return getByIDHandler("providerID", dnsprovider.GetByID)
|
||||
}
|
||||
|
||||
// CreateDNSProvider will create a DNS Provider
|
||||
// Route: POST /dns-providers
|
||||
func CreateDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newItem dnsprovider.Model
|
||||
err := json.Unmarshal(bodyBytes, &newItem)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
||||
newItem.UserID = userID
|
||||
|
||||
if err = newItem.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save DNS Provider: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newItem)
|
||||
createUserOwned(w, r, &dnsprovider.Model{}, "DNS Provider")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateDNSProvider updates a provider
|
||||
// Route: PUT /dns-providers/{providerID}
|
||||
func UpdateDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var providerID uint
|
||||
if providerID, err = getURLParamInt(r, "providerID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := dnsprovider.GetByID(providerID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &item)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = item.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return updateByIDHandler("providerID", dnsprovider.GetByID, (*dnsprovider.Model).Save)
|
||||
}
|
||||
|
||||
// DeleteDNSProvider removes a provider
|
||||
// Route: DELETE /dns-providers/{providerID}
|
||||
func DeleteDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var providerID uint
|
||||
if providerID, err = getURLParamInt(r, "providerID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := dnsprovider.GetByID(providerID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return deleteByIDHandler("providerID", dnsprovider.GetByID, (*dnsprovider.Model).Delete)
|
||||
}
|
||||
|
||||
// GetAcmeshProviders will return a list of acme.sh providers
|
||||
|
||||
@@ -172,34 +172,7 @@ func DeleteHost() func(http.ResponseWriter, *http.Request) {
|
||||
// Route: GET /hosts/{hostID}/nginx-config
|
||||
// Route: GET /hosts/{hostID}/nginx-config.txt
|
||||
func GetHostNginxConfig(format string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID uint
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := host.GetByID(hostID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
// Get the config from disk
|
||||
content, nErr := nginx.GetHostConfigContent(item)
|
||||
if nErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, nErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
if format == "text" {
|
||||
h.ResultResponseText(w, http.StatusOK, content)
|
||||
return
|
||||
}
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, content)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return getNginxConfig(format, "hostID", host.GetByID, nginx.GetHostConfigContent)
|
||||
}
|
||||
|
||||
func configureHost(hst host.Model) {
|
||||
|
||||
@@ -1,142 +1,40 @@
|
||||
//nolint:dupl // thin wiring of the shared CRUD helpers in crud.go
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/nginxtemplate"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GetNginxTemplates will return a list of Nginx Templates
|
||||
// Route: GET /nginx-templates
|
||||
func GetNginxTemplates() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
items, err := nginxtemplate.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, items)
|
||||
}
|
||||
}
|
||||
return listHandler(nginxtemplate.List)
|
||||
}
|
||||
|
||||
// GetNginxTemplate will return a single Nginx Template
|
||||
// Route: GET /nginx-templates/{templateID}
|
||||
func GetNginxTemplate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var templateID uint
|
||||
if templateID, err = getURLParamInt(r, "templateID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := nginxtemplate.GetByID(templateID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return getByIDHandler("templateID", nginxtemplate.GetByID)
|
||||
}
|
||||
|
||||
// CreateNginxTemplate will create a Nginx Template
|
||||
// Route: POST /nginx-templates
|
||||
func CreateNginxTemplate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newNginxTemplate nginxtemplate.Model
|
||||
err := json.Unmarshal(bodyBytes, &newNginxTemplate)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
||||
newNginxTemplate.UserID = userID
|
||||
|
||||
if err = newNginxTemplate.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Nginx Template: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newNginxTemplate)
|
||||
createUserOwned(w, r, &nginxtemplate.Model{}, "Nginx Template")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateNginxTemplate updates a nginx template
|
||||
// Route: PUT /nginx-templates/{templateID}
|
||||
func UpdateNginxTemplate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var templateID uint
|
||||
if templateID, err = getURLParamInt(r, "templateID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// reconfigure, _ := getQueryVarBool(r, "reconfigure", false, false)
|
||||
|
||||
nginxTemplate, err := nginxtemplate.GetByID(templateID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &nginxTemplate)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = nginxTemplate.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, nginxTemplate)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return updateByIDHandler("templateID", nginxtemplate.GetByID, (*nginxtemplate.Model).Save)
|
||||
}
|
||||
|
||||
// DeleteNginxTemplate removes a nginx template
|
||||
// Route: DELETE /nginx-templates/{templateID}
|
||||
func DeleteNginxTemplate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var templateID uint
|
||||
if templateID, err = getURLParamInt(r, "templateID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := nginxtemplate.GetByID(templateID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return deleteByIDHandler("templateID", nginxtemplate.GetByID, (*nginxtemplate.Model).Delete)
|
||||
}
|
||||
@@ -35,12 +35,13 @@ func NotFound() func(http.ResponseWriter, *http.Request) {
|
||||
}
|
||||
|
||||
err := tryRead(assetsSub, path, w)
|
||||
if err == errIsDir {
|
||||
switch err {
|
||||
case errIsDir:
|
||||
err = tryRead(assetsSub, defaultFile, w)
|
||||
if err != nil {
|
||||
h.NotFound(w, r)
|
||||
}
|
||||
} else if err == nil {
|
||||
case nil:
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/entity/auth"
|
||||
@@ -15,17 +14,15 @@ import (
|
||||
njwt "npm/internal/jwt"
|
||||
"npm/internal/logger"
|
||||
|
||||
chiMiddleware "github.com/go-chi/chi/v5/middleware"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// getRequestIPAddress will use X-FORWARDED-FOR header if it exists
|
||||
// otherwise it will use RemoteAddr
|
||||
// getRequestIPAddress will use the client IP set by the router's client IP
|
||||
// middleware if it exists, otherwise it will use RemoteAddr
|
||||
func getRequestIPAddress(r *http.Request) string {
|
||||
// this Get is case insensitive
|
||||
xff := r.Header.Get("X-FORWARDED-FOR")
|
||||
if xff != "" {
|
||||
ip, _, _ := strings.Cut(xff, ",")
|
||||
return strings.TrimSpace(ip)
|
||||
if ip := chiMiddleware.GetClientIP(r.Context()); ip != "" {
|
||||
return ip
|
||||
}
|
||||
return r.RemoteAddr
|
||||
}
|
||||
|
||||
@@ -1,140 +1,40 @@
|
||||
//nolint:dupl // thin wiring of the shared CRUD helpers in crud.go
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/stream"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GetStreams will return a list of Streams
|
||||
// Route: GET /hosts/streams
|
||||
func GetStreams() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
hosts, err := stream.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, hosts)
|
||||
}
|
||||
}
|
||||
return listHandler(stream.List)
|
||||
}
|
||||
|
||||
// GetStream will return a single Streams
|
||||
// Route: GET /hosts/streams/{hostID}
|
||||
func GetStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID uint
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := stream.GetByID(hostID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return getByIDHandler("hostID", stream.GetByID)
|
||||
}
|
||||
|
||||
// CreateStream will create a Stream
|
||||
// Route: POST /hosts/steams
|
||||
func CreateStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newHost stream.Model
|
||||
err := json.Unmarshal(bodyBytes, &newHost)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
||||
newHost.UserID = userID
|
||||
|
||||
if err = newHost.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Stream: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newHost)
|
||||
createUserOwned(w, r, &stream.Model{}, "Stream")
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateStream updates a stream
|
||||
// Route: PUT /hosts/streams/{hostID}
|
||||
func UpdateStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID uint
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
host, err := stream.GetByID(hostID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &host)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = host.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, host)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return updateByIDHandler("hostID", stream.GetByID, (*stream.Model).Save)
|
||||
}
|
||||
|
||||
// DeleteStream removes a stream
|
||||
// Route: DELETE /hosts/streams/{hostID}
|
||||
func DeleteStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID uint
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := stream.GetByID(hostID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return deleteByIDHandler("hostID", stream.GetByID, (*stream.Model).Delete)
|
||||
}
|
||||
@@ -171,34 +171,7 @@ func DeleteUpstream() func(http.ResponseWriter, *http.Request) {
|
||||
// Route: GET /upstreams/{upstreamID}/nginx-config
|
||||
// Route: GET /upstreams/{upstreamID}/nginx-config.txt
|
||||
func GetUpstreamNginxConfig(format string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var upstreamID uint
|
||||
if upstreamID, err = getURLParamInt(r, "upstreamID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := upstream.GetByID(upstreamID)
|
||||
switch err {
|
||||
case gorm.ErrRecordNotFound:
|
||||
h.NotFound(w, r)
|
||||
case nil:
|
||||
// Get the config from disk
|
||||
content, nErr := nginx.GetUpstreamConfigContent(item)
|
||||
if nErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, nErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
if format == "text" {
|
||||
h.ResultResponseText(w, http.StatusOK, content)
|
||||
return
|
||||
}
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, content)
|
||||
default:
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
}
|
||||
return getNginxConfig(format, "upstreamID", upstream.GetByID, nginx.GetUpstreamConfigContent)
|
||||
}
|
||||
|
||||
func configureUpstream(u upstream.Model) {
|
||||
|
||||
@@ -70,7 +70,7 @@ func Enforce(permissions ...string) func(http.Handler) http.Handler {
|
||||
|
||||
var userCapabilities []string
|
||||
if found {
|
||||
userCapabilities = cacheItem.([]string)
|
||||
userCapabilities, _ = cacheItem.([]string)
|
||||
} else {
|
||||
// Get from db and store it
|
||||
userCapabilities, err = user.GetCapabilities(userID)
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestBodyContext(t *testing.T) {
|
||||
|
||||
// Create a test handler that checks the context for the body data
|
||||
handler := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
bodyData := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
bodyData, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
assert.Equal(t, body, bodyData)
|
||||
})
|
||||
|
||||
|
||||
@@ -58,16 +58,13 @@ func listQuerySort(
|
||||
return ctx, 0, ""
|
||||
}
|
||||
|
||||
// Split sort fields up in to slice
|
||||
sorts := strings.Split(sortString, ",")
|
||||
for _, sortItem := range sorts {
|
||||
// Split sort fields up
|
||||
for sortItem := range strings.SplitSeq(sortString, ",") {
|
||||
if strings.Contains(sortItem, ".") {
|
||||
theseItems := strings.Split(sortItem, ".")
|
||||
|
||||
switch strings.ToLower(theseItems[1]) {
|
||||
case "desc":
|
||||
fallthrough
|
||||
case "descending":
|
||||
case "desc", "descending":
|
||||
theseItems[1] = "DESC"
|
||||
default:
|
||||
theseItems[1] = "ASC"
|
||||
|
||||
@@ -38,8 +38,8 @@ func SSEAuth(next http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
iss, _ := token.Get("iss")
|
||||
if iss != "sse" {
|
||||
iss, ok := token.Issuer()
|
||||
if !ok || iss != "sse" {
|
||||
h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"npm/internal/api/middleware"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
// resourceRoutes describes the standard list/create/get/update/delete
|
||||
// endpoints for an entity
|
||||
type resourceRoutes struct {
|
||||
// idParam is the url param name for a specific item, eg "hostID"
|
||||
idParam string
|
||||
// model is the entity used to validate list query sorting and filtering
|
||||
model any
|
||||
// viewCapability is required to list and get items
|
||||
viewCapability string
|
||||
// manageCapability is required to create, update and delete items
|
||||
manageCapability string
|
||||
createSchema string
|
||||
// updateSchema is optional, the request body isn't validated when empty
|
||||
updateSchema string
|
||||
list http.HandlerFunc
|
||||
create http.HandlerFunc
|
||||
get http.HandlerFunc
|
||||
update http.HandlerFunc
|
||||
del http.HandlerFunc
|
||||
// manageItem optionally registers extra routes on a specific item that
|
||||
// require manageCapability
|
||||
manageItem func(chi.Router)
|
||||
}
|
||||
|
||||
// mount registers the resource's routes on r
|
||||
func (rr resourceRoutes) mount(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(rr.viewCapability),
|
||||
middleware.ListQuery(rr.model),
|
||||
).Get("/", rr.list)
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(rr.manageCapability), middleware.EnforceRequestSchema(rr.createSchema)).
|
||||
Post("/", rr.create)
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{"+rr.idParam+":[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(rr.viewCapability)).
|
||||
Get("/", rr.get)
|
||||
r.With(middleware.Enforce(rr.manageCapability)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/", rr.del)
|
||||
if rr.updateSchema != "" {
|
||||
r.With(middleware.EnforceRequestSchema(rr.updateSchema)).Put("/", rr.update)
|
||||
} else {
|
||||
r.Put("/", rr.update)
|
||||
}
|
||||
if rr.manageItem != nil {
|
||||
rr.manageItem(r)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
+119
-188
@@ -53,7 +53,9 @@ func NewRouter() http.Handler {
|
||||
corss.Handler,
|
||||
middleware.Cors(r),
|
||||
middleware.Options(r),
|
||||
chiMiddleware.RealIP,
|
||||
// nginx in front of the api overwrites X-Forwarded-For with the
|
||||
// connecting address, so the last value is the only trusted one
|
||||
chiMiddleware.ClientIPFromHeader("X-Forwarded-For"),
|
||||
chiMiddleware.Recoverer,
|
||||
chiMiddleware.Throttle(5),
|
||||
middleware.PrettyPrint,
|
||||
@@ -170,51 +172,35 @@ func applyRoutes(r chi.Router) chi.Router {
|
||||
})
|
||||
|
||||
// Access Lists
|
||||
r.With(middleware.EnforceSetup()).Route("/access-lists", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityAccessListsView),
|
||||
middleware.ListQuery(accesslist.Model{}),
|
||||
).Get("/", handler.GetAccessLists())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityAccessListsManage), middleware.EnforceRequestSchema(schema.CreateAccessList())).
|
||||
Post("/", handler.CreateAccessList())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{accessListID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityAccessListsView)).
|
||||
Get("/", handler.GetAccessList())
|
||||
r.With(middleware.Enforce(user.CapabilityAccessListsManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/{accessListID:[0-9]+}", handler.DeleteAccessList())
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateAccessList())).
|
||||
Put("/{accessListID:[0-9]+}", handler.UpdateAccessList())
|
||||
})
|
||||
})
|
||||
})
|
||||
r.With(middleware.EnforceSetup()).Route("/access-lists", resourceRoutes{
|
||||
idParam: "accessListID",
|
||||
model: accesslist.Model{},
|
||||
viewCapability: user.CapabilityAccessListsView,
|
||||
manageCapability: user.CapabilityAccessListsManage,
|
||||
createSchema: schema.CreateAccessList(),
|
||||
updateSchema: schema.UpdateAccessList(),
|
||||
list: handler.GetAccessLists(),
|
||||
create: handler.CreateAccessList(),
|
||||
get: handler.GetAccessList(),
|
||||
update: handler.UpdateAccessList(),
|
||||
del: handler.DeleteAccessList(),
|
||||
}.mount)
|
||||
|
||||
// DNS Providers
|
||||
r.With(middleware.EnforceSetup()).Route("/dns-providers", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityDNSProvidersView),
|
||||
middleware.ListQuery(dnsprovider.Model{}),
|
||||
).Get("/", handler.GetDNSProviders())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityDNSProvidersManage), middleware.EnforceRequestSchema(schema.CreateDNSProvider())).
|
||||
Post("/", handler.CreateDNSProvider())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{providerID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityDNSProvidersView)).
|
||||
Get("/{providerID:[0-9]+}", handler.GetDNSProvider())
|
||||
r.With(middleware.Enforce(user.CapabilityDNSProvidersManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/", handler.DeleteDNSProvider())
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateDNSProvider())).
|
||||
Put("/{providerID:[0-9]+}", handler.UpdateDNSProvider())
|
||||
})
|
||||
})
|
||||
resourceRoutes{
|
||||
idParam: "providerID",
|
||||
model: dnsprovider.Model{},
|
||||
viewCapability: user.CapabilityDNSProvidersView,
|
||||
manageCapability: user.CapabilityDNSProvidersManage,
|
||||
createSchema: schema.CreateDNSProvider(),
|
||||
updateSchema: schema.UpdateDNSProvider(),
|
||||
list: handler.GetDNSProviders(),
|
||||
create: handler.CreateDNSProvider(),
|
||||
get: handler.GetDNSProvider(),
|
||||
update: handler.UpdateDNSProvider(),
|
||||
del: handler.DeleteDNSProvider(),
|
||||
}.mount(r)
|
||||
|
||||
// List Acme DNS Providers
|
||||
r.With(middleware.Enforce(user.CapabilityDNSProvidersView)).Route("/acmesh", func(r chi.Router) {
|
||||
@@ -224,161 +210,106 @@ func applyRoutes(r chi.Router) chi.Router {
|
||||
})
|
||||
|
||||
// Certificate Authorities
|
||||
r.With(middleware.EnforceSetup()).Route("/certificate-authorities", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityCertificateAuthoritiesView),
|
||||
middleware.ListQuery(certificateauthority.Model{}),
|
||||
).Get("/", handler.GetCertificateAuthorities())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityCertificateAuthoritiesManage), middleware.EnforceRequestSchema(schema.CreateCertificateAuthority())).
|
||||
Post("/", handler.CreateCertificateAuthority())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{caID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityCertificateAuthoritiesView)).
|
||||
Get("/", handler.GetCertificateAuthority())
|
||||
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateCertificateAuthority())).
|
||||
Put("/", handler.UpdateCertificateAuthority())
|
||||
r.With(middleware.Enforce(user.CapabilityCertificateAuthoritiesManage)).
|
||||
Delete("/", handler.DeleteCertificateAuthority())
|
||||
|
||||
r.With(middleware.Enforce(user.CapabilityCertificateAuthoritiesManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/{caID:[0-9]+}", handler.DeleteCertificateAuthority())
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateCertificateAuthority())).
|
||||
Put("/{caID:[0-9]+}", handler.UpdateCertificateAuthority())
|
||||
})
|
||||
})
|
||||
})
|
||||
r.With(middleware.EnforceSetup()).Route("/certificate-authorities", resourceRoutes{
|
||||
idParam: "caID",
|
||||
model: certificateauthority.Model{},
|
||||
viewCapability: user.CapabilityCertificateAuthoritiesView,
|
||||
manageCapability: user.CapabilityCertificateAuthoritiesManage,
|
||||
createSchema: schema.CreateCertificateAuthority(),
|
||||
updateSchema: schema.UpdateCertificateAuthority(),
|
||||
list: handler.GetCertificateAuthorities(),
|
||||
create: handler.CreateCertificateAuthority(),
|
||||
get: handler.GetCertificateAuthority(),
|
||||
update: handler.UpdateCertificateAuthority(),
|
||||
del: handler.DeleteCertificateAuthority(),
|
||||
}.mount)
|
||||
|
||||
// Certificates
|
||||
r.With(middleware.EnforceSetup()).Route("/certificates", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityCertificatesView),
|
||||
middleware.ListQuery(certificate.Model{}),
|
||||
).Get("/", handler.GetCertificates())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityCertificatesManage), middleware.EnforceRequestSchema(schema.CreateCertificate())).
|
||||
Post("/", handler.CreateCertificate())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{certificateID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityCertificatesView)).
|
||||
Get("/", handler.GetCertificate())
|
||||
r.With(middleware.Enforce(user.CapabilityCertificatesManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/", handler.DeleteCertificate())
|
||||
r.Put("/", handler.UpdateCertificate())
|
||||
// r.With(middleware.EnforceRequestSchema(schema.UpdateCertificate())).
|
||||
// Put("/", handler.UpdateCertificate())
|
||||
r.Post("/renew", handler.RenewCertificate())
|
||||
r.Get("/download", handler.DownloadCertificate())
|
||||
})
|
||||
})
|
||||
})
|
||||
r.With(middleware.EnforceSetup()).Route("/certificates", resourceRoutes{
|
||||
idParam: "certificateID",
|
||||
model: certificate.Model{},
|
||||
viewCapability: user.CapabilityCertificatesView,
|
||||
manageCapability: user.CapabilityCertificatesManage,
|
||||
createSchema: schema.CreateCertificate(),
|
||||
// updateSchema: schema.UpdateCertificate(),
|
||||
list: handler.GetCertificates(),
|
||||
create: handler.CreateCertificate(),
|
||||
get: handler.GetCertificate(),
|
||||
update: handler.UpdateCertificate(),
|
||||
del: handler.DeleteCertificate(),
|
||||
manageItem: func(r chi.Router) {
|
||||
r.Post("/renew", handler.RenewCertificate())
|
||||
r.Get("/download", handler.DownloadCertificate())
|
||||
},
|
||||
}.mount)
|
||||
|
||||
// Hosts
|
||||
r.With(middleware.EnforceSetup()).Route("/hosts", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityHostsView),
|
||||
middleware.ListQuery(host.Model{}),
|
||||
).Get("/", handler.GetHosts())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityHostsManage), middleware.EnforceRequestSchema(schema.CreateHost())).
|
||||
Post("/", handler.CreateHost())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{hostID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityHostsView)).
|
||||
Get("/", handler.GetHost())
|
||||
r.With(middleware.Enforce(user.CapabilityHostsManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/", handler.DeleteHost())
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateHost())).
|
||||
Put("/", handler.UpdateHost())
|
||||
r.Get("/nginx-config", handler.GetHostNginxConfig("json"))
|
||||
r.Get("/nginx-config.txt", handler.GetHostNginxConfig("text"))
|
||||
})
|
||||
})
|
||||
})
|
||||
r.With(middleware.EnforceSetup()).Route("/hosts", resourceRoutes{
|
||||
idParam: "hostID",
|
||||
model: host.Model{},
|
||||
viewCapability: user.CapabilityHostsView,
|
||||
manageCapability: user.CapabilityHostsManage,
|
||||
createSchema: schema.CreateHost(),
|
||||
updateSchema: schema.UpdateHost(),
|
||||
list: handler.GetHosts(),
|
||||
create: handler.CreateHost(),
|
||||
get: handler.GetHost(),
|
||||
update: handler.UpdateHost(),
|
||||
del: handler.DeleteHost(),
|
||||
manageItem: func(r chi.Router) {
|
||||
r.Get("/nginx-config", handler.GetHostNginxConfig("json"))
|
||||
r.Get("/nginx-config.txt", handler.GetHostNginxConfig("text"))
|
||||
},
|
||||
}.mount)
|
||||
|
||||
// Nginx Templates
|
||||
r.With(middleware.EnforceSetup()).Route("/nginx-templates", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityNginxTemplatesView),
|
||||
middleware.ListQuery(nginxtemplate.Model{}),
|
||||
).Get("/", handler.GetNginxTemplates())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityNginxTemplatesManage), middleware.EnforceRequestSchema(schema.CreateNginxTemplate())).
|
||||
Post("/", handler.CreateNginxTemplate())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{templateID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityNginxTemplatesView)).
|
||||
Get("/", handler.GetNginxTemplates())
|
||||
r.With(middleware.Enforce(user.CapabilityHostsManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/", handler.DeleteNginxTemplate())
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateNginxTemplate())).
|
||||
Put("/", handler.UpdateNginxTemplate())
|
||||
})
|
||||
})
|
||||
})
|
||||
r.With(middleware.EnforceSetup()).Route("/nginx-templates", resourceRoutes{
|
||||
idParam: "templateID",
|
||||
model: nginxtemplate.Model{},
|
||||
viewCapability: user.CapabilityNginxTemplatesView,
|
||||
manageCapability: user.CapabilityNginxTemplatesManage,
|
||||
createSchema: schema.CreateNginxTemplate(),
|
||||
updateSchema: schema.UpdateNginxTemplate(),
|
||||
list: handler.GetNginxTemplates(),
|
||||
create: handler.CreateNginxTemplate(),
|
||||
get: handler.GetNginxTemplate(),
|
||||
update: handler.UpdateNginxTemplate(),
|
||||
del: handler.DeleteNginxTemplate(),
|
||||
}.mount)
|
||||
|
||||
// Streams
|
||||
r.With(middleware.EnforceSetup()).Route("/streams", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityStreamsView),
|
||||
middleware.ListQuery(stream.Model{}),
|
||||
).Get("/", handler.GetStreams())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityStreamsManage), middleware.EnforceRequestSchema(schema.CreateStream())).
|
||||
Post("/", handler.CreateStream())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{hostID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityStreamsView)).
|
||||
Get("/", handler.GetStream())
|
||||
r.With(middleware.Enforce(user.CapabilityHostsManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/", handler.DeleteStream())
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateStream())).
|
||||
Put("/", handler.UpdateStream())
|
||||
})
|
||||
})
|
||||
})
|
||||
r.With(middleware.EnforceSetup()).Route("/streams", resourceRoutes{
|
||||
idParam: "hostID",
|
||||
model: stream.Model{},
|
||||
viewCapability: user.CapabilityStreamsView,
|
||||
manageCapability: user.CapabilityStreamsManage,
|
||||
createSchema: schema.CreateStream(),
|
||||
updateSchema: schema.UpdateStream(),
|
||||
list: handler.GetStreams(),
|
||||
create: handler.CreateStream(),
|
||||
get: handler.GetStream(),
|
||||
update: handler.UpdateStream(),
|
||||
del: handler.DeleteStream(),
|
||||
}.mount)
|
||||
|
||||
// Upstreams
|
||||
r.With(middleware.EnforceSetup()).Route("/upstreams", func(r chi.Router) {
|
||||
// List
|
||||
r.With(
|
||||
middleware.Enforce(user.CapabilityHostsView),
|
||||
middleware.ListQuery(upstream.Model{}),
|
||||
).Get("/", handler.GetUpstreams())
|
||||
|
||||
// Create
|
||||
r.With(middleware.Enforce(user.CapabilityHostsManage), middleware.EnforceRequestSchema(schema.CreateUpstream())).
|
||||
Post("/", handler.CreateUpstream())
|
||||
|
||||
// Specific Item
|
||||
r.Route("/{upstreamID:[0-9]+}", func(r chi.Router) {
|
||||
r.With(middleware.Enforce(user.CapabilityHostsView)).
|
||||
Get("/", handler.GetUpstream())
|
||||
r.With(middleware.Enforce(user.CapabilityHostsManage)).Route("/", func(r chi.Router) {
|
||||
r.Delete("/", handler.DeleteUpstream())
|
||||
r.With(middleware.EnforceRequestSchema(schema.UpdateUpstream())).
|
||||
Put("/", handler.UpdateUpstream())
|
||||
r.Get("/nginx-config", handler.GetUpstreamNginxConfig("json"))
|
||||
r.Get("/nginx-config.txt", handler.GetUpstreamNginxConfig("text"))
|
||||
})
|
||||
})
|
||||
})
|
||||
r.With(middleware.EnforceSetup()).Route("/upstreams", resourceRoutes{
|
||||
idParam: "upstreamID",
|
||||
model: upstream.Model{},
|
||||
viewCapability: user.CapabilityHostsView,
|
||||
manageCapability: user.CapabilityHostsManage,
|
||||
createSchema: schema.CreateUpstream(),
|
||||
updateSchema: schema.UpdateUpstream(),
|
||||
list: handler.GetUpstreams(),
|
||||
create: handler.CreateUpstream(),
|
||||
get: handler.GetUpstream(),
|
||||
update: handler.UpdateUpstream(),
|
||||
del: handler.DeleteUpstream(),
|
||||
manageItem: func(r chi.Router) {
|
||||
r.Get("/nginx-config", handler.GetUpstreamNginxConfig("json"))
|
||||
r.Get("/nginx-config.txt", handler.GetUpstreamNginxConfig("text"))
|
||||
},
|
||||
}.mount)
|
||||
})
|
||||
|
||||
return r
|
||||
|
||||
@@ -3,7 +3,6 @@ package api
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"npm/internal/config"
|
||||
@@ -21,8 +20,7 @@ var (
|
||||
// Tear up/down
|
||||
func TestMain(m *testing.M) {
|
||||
config.Init(&version, &commit)
|
||||
code := m.Run()
|
||||
os.Exit(code)
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestGetHealthz(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,23 @@ import (
|
||||
"npm/internal/errors"
|
||||
)
|
||||
|
||||
// JSON schema types and common field titles used by providers
|
||||
const (
|
||||
typeString = "string"
|
||||
typeObject = "object"
|
||||
typeInteger = "integer"
|
||||
typeBoolean = "boolean"
|
||||
|
||||
titleAPIKey = "api-key"
|
||||
titleAPIURL = "api-url"
|
||||
titleKey = "key"
|
||||
titlePassword = "password"
|
||||
titleSecret = "secret"
|
||||
titleToken = "token"
|
||||
titleUser = "user"
|
||||
titleUsername = "username"
|
||||
)
|
||||
|
||||
// providerField should mimick jsonschema, so that
|
||||
// the ui can render a field and validate it
|
||||
// before we do.
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSAcmeDNS() Provider {
|
||||
return Provider{
|
||||
Title: "dns_acmedns",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"ACMEDNS_BASE_URL",
|
||||
@@ -14,19 +14,19 @@ func getDNSAcmeDNS() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"ACMEDNS_BASE_URL": {
|
||||
Title: "base-url",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
},
|
||||
"ACMEDNS_SUBDOMAIN": {
|
||||
Title: "subdomain",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
},
|
||||
"ACMEDNS_USERNAME": {
|
||||
Title: "username",
|
||||
Type: "string",
|
||||
Title: titleUsername,
|
||||
Type: typeString,
|
||||
},
|
||||
"ACMEDNS_PASSWORD": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
IsSecret: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSAd() Provider {
|
||||
return Provider{
|
||||
Title: "dns_ad",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"AD_API_KEY",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"AD_API_KEY": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSAli() Provider {
|
||||
return Provider{
|
||||
Title: "dns_ali",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"Ali_Key",
|
||||
@@ -11,13 +11,13 @@ func getDNSAli() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"Ali_Key": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"Ali_Secret": {
|
||||
Title: "secret",
|
||||
Type: "string",
|
||||
Title: titleSecret,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSAutoDNS() Provider {
|
||||
return Provider{
|
||||
Title: "dns_autodns",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"AUTODNS_USER",
|
||||
@@ -12,19 +12,19 @@ func getDNSAutoDNS() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"AUTODNS_USER": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"AUTODNS_PASSWORD": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
"AUTODNS_CONTEXT": {
|
||||
Title: "context",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSAws() Provider {
|
||||
return Provider{
|
||||
Title: "dns_aws",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"AWS_ACCESS_KEY_ID",
|
||||
@@ -12,18 +12,18 @@ func getDNSAws() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"AWS_ACCESS_KEY_ID": {
|
||||
Title: "access-key-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 10,
|
||||
},
|
||||
"AWS_SECRET_ACCESS_KEY": {
|
||||
Title: "secret-access-key",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 10,
|
||||
IsSecret: true,
|
||||
},
|
||||
"AWS_DNS_SLOWRATE": {
|
||||
Title: "slow-rate",
|
||||
Type: "integer",
|
||||
Type: typeInteger,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSAzure() Provider {
|
||||
return Provider{
|
||||
Title: "dns_azure",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"AZUREDNS_SUBSCRIPTIONID",
|
||||
@@ -14,22 +14,22 @@ func getDNSAzure() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"AZUREDNS_SUBSCRIPTIONID": {
|
||||
Title: "subscription-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"AZUREDNS_TENANTID": {
|
||||
Title: "tenant-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"AZUREDNS_APPID": {
|
||||
Title: "app-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"AZUREDNS_CLIENTSECRET": {
|
||||
Title: "client-secret",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSCf() Provider {
|
||||
return Provider{
|
||||
Title: "dns_cf",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"CF_Key",
|
||||
@@ -13,29 +13,29 @@ func getDNSCf() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"CF_Key": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"CF_Email": {
|
||||
Title: "email",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 5,
|
||||
},
|
||||
"CF_Token": {
|
||||
Title: "token",
|
||||
Type: "string",
|
||||
Title: titleToken,
|
||||
Type: typeString,
|
||||
MinLength: 5,
|
||||
IsSecret: true,
|
||||
},
|
||||
"CF_Account_ID": {
|
||||
Title: "account-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"CF_Zone_ID": {
|
||||
Title: "zone-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSCloudns() Provider {
|
||||
return Provider{
|
||||
Title: "dns_cloudns",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"CLOUDNS_AUTH_ID",
|
||||
@@ -13,17 +13,17 @@ func getDNSCloudns() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"CLOUDNS_AUTH_ID": {
|
||||
Title: "auth-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"CLOUDNS_SUB_AUTH_ID": {
|
||||
Title: "sub-auth-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"CLOUDNS_AUTH_PASSWORD": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSConoha() Provider {
|
||||
return Provider{
|
||||
Title: "dns_conoha",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"CONOHA_IdentityServiceApi",
|
||||
@@ -13,24 +13,24 @@ func getDNSConoha() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"CONOHA_IdentityServiceApi": {
|
||||
Title: "api-url",
|
||||
Type: "string",
|
||||
Title: titleAPIURL,
|
||||
Type: typeString,
|
||||
MinLength: 4,
|
||||
},
|
||||
"CONOHA_Username": {
|
||||
Title: "username",
|
||||
Type: "string",
|
||||
Title: titleUsername,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"CONOHA_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
"CONOHA_TenantId": {
|
||||
Title: "tenant-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSCx() Provider {
|
||||
return Provider{
|
||||
Title: "dns_cx",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"CX_Key",
|
||||
@@ -11,12 +11,12 @@ func getDNSCx() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"CX_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
},
|
||||
"CX_Secret": {
|
||||
Title: "secret",
|
||||
Type: "string",
|
||||
Title: titleSecret,
|
||||
Type: typeString,
|
||||
IsSecret: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSCyon() Provider {
|
||||
return Provider{
|
||||
Title: "dns_cyon",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"CY_Username",
|
||||
@@ -12,19 +12,19 @@ func getDNSCyon() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"CY_Username": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"CY_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
"CY_OTP_Secret": {
|
||||
Title: "otp-secret",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,20 +3,20 @@ package dnsproviders
|
||||
func getDNSDa() Provider {
|
||||
return Provider{
|
||||
Title: "dns_da",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DA_Api",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"DA_Api": {
|
||||
Title: "api-url",
|
||||
Type: "string",
|
||||
Title: titleAPIURL,
|
||||
Type: typeString,
|
||||
MinLength: 4,
|
||||
},
|
||||
"DA_Api_Insecure": {
|
||||
Title: "insecure",
|
||||
Type: "boolean",
|
||||
Type: typeBoolean,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSDgon() Provider {
|
||||
return Provider{
|
||||
Title: "dns_dgon",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DO_API_KEY",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"DO_API_KEY": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
IsSecret: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSDNSimple() Provider {
|
||||
return Provider{
|
||||
Title: "dns_dnsimple",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DNSimple_OAUTH_TOKEN",
|
||||
@@ -11,7 +11,7 @@ func getDNSDNSimple() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"DNSimple_OAUTH_TOKEN": {
|
||||
Title: "oauth-token",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSDp() Provider {
|
||||
return Provider{
|
||||
Title: "dns_dp",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DP_Id",
|
||||
@@ -12,12 +12,12 @@ func getDNSDp() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"DP_Id": {
|
||||
Title: "id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"DP_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSDpi() Provider {
|
||||
return Provider{
|
||||
Title: "dns_dpi",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DPI_Id",
|
||||
@@ -12,12 +12,12 @@ func getDNSDpi() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"DPI_Id": {
|
||||
Title: "id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"DPI_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSDreamhost() Provider {
|
||||
return Provider{
|
||||
Title: "dns_dreamhost",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DH_API_KEY",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"DH_API_KEY": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSDuckDNS() Provider {
|
||||
return Provider{
|
||||
Title: "dns_duckdns",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DuckDNS_Token",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"DuckDNS_Token": {
|
||||
Title: "token",
|
||||
Type: "string",
|
||||
Title: titleToken,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSDyn() Provider {
|
||||
return Provider{
|
||||
Title: "dns_dyn",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"DYN_Customer",
|
||||
@@ -13,17 +13,17 @@ func getDNSDyn() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"DYN_Customer": {
|
||||
Title: "customer",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"DYN_Username": {
|
||||
Title: "username",
|
||||
Type: "string",
|
||||
Title: titleUsername,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"DYN_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSDynu() Provider {
|
||||
return Provider{
|
||||
Title: "dns_dynu",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"Dynu_ClientId",
|
||||
@@ -11,12 +11,12 @@ func getDNSDynu() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"Dynu_ClientId": {
|
||||
Title: "client-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"Dynu_Secret": {
|
||||
Title: "secret",
|
||||
Type: "string",
|
||||
Title: titleSecret,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSEuserv() Provider {
|
||||
return Provider{
|
||||
Title: "dns_euserv",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"EUSERV_Username",
|
||||
@@ -11,13 +11,13 @@ func getDNSEuserv() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"EUSERV_Username": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"EUSERV_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSFreeDNS() Provider {
|
||||
return Provider{
|
||||
Title: "dns_freedns",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"FREEDNS_User",
|
||||
@@ -11,13 +11,13 @@ func getDNSFreeDNS() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"FREEDNS_User": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"FREEDNS_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSGandiLiveDNS() Provider {
|
||||
return Provider{
|
||||
Title: "dns_gandi_livedns",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"GANDI_LIVEDNS_KEY",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"GANDI_LIVEDNS_KEY": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSGd() Provider {
|
||||
return Provider{
|
||||
Title: "dns_gd",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"GD_Key",
|
||||
@@ -11,13 +11,13 @@ func getDNSGd() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"GD_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"GD_Secret": {
|
||||
Title: "secret",
|
||||
Type: "string",
|
||||
Title: titleSecret,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSHe() Provider {
|
||||
return Provider{
|
||||
Title: "dns_he",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"HE_Username",
|
||||
@@ -11,13 +11,13 @@ func getDNSHe() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"HE_Username": {
|
||||
Title: "username",
|
||||
Type: "string",
|
||||
Title: titleUsername,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"HE_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSInfoblox() Provider {
|
||||
return Provider{
|
||||
Title: "dns_infoblox",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"Infoblox_Creds",
|
||||
@@ -12,13 +12,13 @@ func getDNSInfoblox() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"Infoblox_Creds": {
|
||||
Title: "credentials",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
"Infoblox_Server": {
|
||||
Title: "server",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSInwx() Provider {
|
||||
return Provider{
|
||||
Title: "dns_inwx",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"INWX_User",
|
||||
@@ -11,13 +11,13 @@ func getDNSInwx() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"INWX_User": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"INWX_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSIspconfig() Provider {
|
||||
return Provider{
|
||||
Title: "dns_ispconfig",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"ISPC_User",
|
||||
@@ -12,24 +12,24 @@ func getDNSIspconfig() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"ISPC_User": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"ISPC_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
"ISPC_Api": {
|
||||
Title: "api-url",
|
||||
Type: "string",
|
||||
Title: titleAPIURL,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"ISPC_Api_Insecure": {
|
||||
Title: "insecure",
|
||||
Type: "boolean",
|
||||
Type: typeBoolean,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSKinghost() Provider {
|
||||
return Provider{
|
||||
Title: "dns_kinghost",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"KINGHOST_Username",
|
||||
@@ -11,13 +11,13 @@ func getDNSKinghost() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"KINGHOST_Username": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"KINGHOST_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -5,15 +5,15 @@ package dnsproviders
|
||||
func getDNSLinodeV4() Provider {
|
||||
return Provider{
|
||||
Title: "dns_linode_v4",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"LINODE_V4_API_KEY",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"LINODE_V4_API_KEY": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSLoopia() Provider {
|
||||
return Provider{
|
||||
Title: "dns_loopia",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"LOOPIA_Api",
|
||||
@@ -12,18 +12,18 @@ func getDNSLoopia() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"LOOPIA_Api": {
|
||||
Title: "api-url",
|
||||
Type: "string",
|
||||
Title: titleAPIURL,
|
||||
Type: typeString,
|
||||
MinLength: 4,
|
||||
},
|
||||
"LOOPIA_User": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"LOOPIA_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSLua() Provider {
|
||||
return Provider{
|
||||
Title: "dns_lua",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"LUA_Key",
|
||||
@@ -11,14 +11,14 @@ func getDNSLua() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"LUA_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
"LUA_Email": {
|
||||
Title: "email",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 5,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSMe() Provider {
|
||||
return Provider{
|
||||
Title: "dns_me",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"ME_Key",
|
||||
@@ -11,13 +11,13 @@ func getDNSMe() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"ME_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"ME_Secret": {
|
||||
Title: "secret",
|
||||
Type: "string",
|
||||
Title: titleSecret,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSNamecom() Provider {
|
||||
return Provider{
|
||||
Title: "dns_namecom",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"Namecom_Username",
|
||||
@@ -11,13 +11,13 @@ func getDNSNamecom() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"Namecom_Username": {
|
||||
Title: "username",
|
||||
Type: "string",
|
||||
Title: titleUsername,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"Namecom_Token": {
|
||||
Title: "token",
|
||||
Type: "string",
|
||||
Title: titleToken,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSNamesilo() Provider {
|
||||
return Provider{
|
||||
Title: "dns_namesilo",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"Namesilo_Key",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"Namesilo_Key": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSOne() Provider {
|
||||
return Provider{
|
||||
Title: "dns_nsone",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"NS1_Key",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"NS1_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSPDNS() Provider {
|
||||
return Provider{
|
||||
Title: "dns_pdns",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"PDNS_Url",
|
||||
@@ -14,22 +14,22 @@ func getDNSPDNS() Provider {
|
||||
Properties: map[string]providerField{
|
||||
"PDNS_Url": {
|
||||
Title: "url",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"PDNS_ServerId": {
|
||||
Title: "server-id",
|
||||
Type: "string",
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"PDNS_Token": {
|
||||
Title: "token",
|
||||
Type: "string",
|
||||
Title: titleToken,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"PDNS_Ttl": {
|
||||
Title: "ttl",
|
||||
Type: "integer",
|
||||
Type: typeInteger,
|
||||
Minimum: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSSelectel() Provider {
|
||||
return Provider{
|
||||
Title: "dns_selectel",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"SL_Key",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"SL_Key": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSServercow() Provider {
|
||||
return Provider{
|
||||
Title: "dns_servercow",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"SERVERCOW_API_Username",
|
||||
@@ -11,13 +11,13 @@ func getDNSServercow() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"SERVERCOW_API_Username": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"SERVERCOW_API_Password": {
|
||||
Title: "password",
|
||||
Type: "string",
|
||||
Title: titlePassword,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSTele3() Provider {
|
||||
return Provider{
|
||||
Title: "dns_tele3",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"TELE3_Key",
|
||||
@@ -11,13 +11,13 @@ func getDNSTele3() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"TELE3_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
"TELE3_Secret": {
|
||||
Title: "secret",
|
||||
Type: "string",
|
||||
Title: titleSecret,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ package dnsproviders
|
||||
func getDNSUnoeuro() Provider {
|
||||
return Provider{
|
||||
Title: "dns_unoeuro",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"UNO_Key",
|
||||
@@ -11,14 +11,14 @@ func getDNSUnoeuro() Provider {
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"UNO_Key": {
|
||||
Title: "key",
|
||||
Type: "string",
|
||||
Title: titleKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
"UNO_User": {
|
||||
Title: "user",
|
||||
Type: "string",
|
||||
Title: titleUser,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSVscale() Provider {
|
||||
return Provider{
|
||||
Title: "dns_vscale",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"VSCALE_API_KEY",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"VSCALE_API_KEY": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSYandex() Provider {
|
||||
return Provider{
|
||||
Title: "dns_yandex",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"PDD_Token",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"PDD_Token": {
|
||||
Title: "token",
|
||||
Type: "string",
|
||||
Title: titleToken,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSDNZilore() Provider {
|
||||
return Provider{
|
||||
Title: "dns_zilore",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"Zilore_Key",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"Zilore_Key": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -3,15 +3,15 @@ package dnsproviders
|
||||
func getDNSZonomi() Provider {
|
||||
return Provider{
|
||||
Title: "dns_zonomi",
|
||||
Type: "object",
|
||||
Type: typeObject,
|
||||
AdditionalProperties: false,
|
||||
Required: []string{
|
||||
"ZM_Key",
|
||||
},
|
||||
Properties: map[string]providerField{
|
||||
"ZM_Key": {
|
||||
Title: "api-key",
|
||||
Type: "string",
|
||||
Title: titleAPIKey,
|
||||
Type: typeString,
|
||||
MinLength: 1,
|
||||
IsSecret: true,
|
||||
},
|
||||
|
||||
@@ -31,6 +31,11 @@ func (m *Model) LoadByID(id uint) error {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// SetUserID sets the owner of this model
|
||||
func (m *Model) SetUserID(userID uint) {
|
||||
m.UserID = userID
|
||||
}
|
||||
|
||||
// Save will save this model to the DB
|
||||
func (m *Model) Save() error {
|
||||
if m.UserID == 0 {
|
||||
|
||||
@@ -41,7 +41,8 @@ type OAuthUser struct {
|
||||
func (m *OAuthUser) GetResourceField(field string) string {
|
||||
if m.Resource != nil {
|
||||
if value, ok := m.Resource[field]; ok {
|
||||
return value.(string)
|
||||
str, _ := value.(string)
|
||||
return str
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
||||
@@ -210,9 +210,7 @@ func (s *testsuite) TestDelete() {
|
||||
assert.Equal(s.T(), "Unable to delete a new object", err.Error())
|
||||
|
||||
m2 := Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
}
|
||||
err2 := m2.Delete()
|
||||
require.NoError(s.T(), err2)
|
||||
|
||||
@@ -296,9 +296,7 @@ func (s *testsuite) TestDelete() {
|
||||
assert.Equal(s.T(), "Unable to delete a new object", err.Error())
|
||||
|
||||
m2 := Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
}
|
||||
err2 := m2.Delete()
|
||||
require.NoError(s.T(), err2)
|
||||
|
||||
@@ -34,6 +34,11 @@ func (m *Model) LoadByID(id uint) error {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// SetUserID sets the owner of this model
|
||||
func (m *Model) SetUserID(userID uint) {
|
||||
m.UserID = userID
|
||||
}
|
||||
|
||||
// Save will save this model to the DB
|
||||
func (m *Model) Save() error {
|
||||
if m.UserID == 0 {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"npm/internal/model"
|
||||
"npm/internal/status"
|
||||
"npm/internal/test"
|
||||
"npm/internal/types"
|
||||
@@ -189,9 +188,7 @@ func (s *testsuite) TestDelete() {
|
||||
assert.Equal(s.T(), "Unable to delete a new object", err.Error())
|
||||
|
||||
m2 := Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
}
|
||||
err2 := m2.Delete()
|
||||
require.NoError(s.T(), err2)
|
||||
@@ -203,11 +200,9 @@ func (s *testsuite) TestGetTemplate() {
|
||||
defer goleak.VerifyNone(s.T(), goleak.IgnoreAnyFunction("database/sql.(*DB).connectionOpener"))
|
||||
|
||||
m := Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
CreatedAt: time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
||||
UpdatedAt: time.Date(2018, 8, 12, 7, 30, 24, 16, time.UTC).UnixMilli(),
|
||||
},
|
||||
ID: 10,
|
||||
CreatedAt: time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
||||
UpdatedAt: time.Date(2018, 8, 12, 7, 30, 24, 16, time.UTC).UnixMilli(),
|
||||
UserID: 100,
|
||||
Type: "proxy",
|
||||
NginxTemplateID: 20,
|
||||
|
||||
@@ -23,9 +23,7 @@ func ListQueryBuilder(
|
||||
filters []model.Filter,
|
||||
filterMap map[string]model.FilterMapValue,
|
||||
) *gorm.DB {
|
||||
scopes := make([]func(*gorm.DB) *gorm.DB, 0)
|
||||
scopes = append(scopes, ScopeFilters(filters, filterMap))
|
||||
return database.GetDB().Scopes(scopes...)
|
||||
return database.GetDB().Scopes(ScopeFilters(filters, filterMap))
|
||||
}
|
||||
|
||||
// AddOrderToList is used after query above is used for counting
|
||||
|
||||
@@ -28,6 +28,11 @@ func (m *Model) LoadByID(id uint) error {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// SetUserID sets the owner of this model
|
||||
func (m *Model) SetUserID(userID uint) {
|
||||
m.UserID = userID
|
||||
}
|
||||
|
||||
// Save will save this model to the DB
|
||||
func (m *Model) Save() error {
|
||||
if m.UserID == 0 {
|
||||
|
||||
@@ -96,7 +96,7 @@ func ScopeFilters(filters []model.Filter, filterMap map[string]model.FilterMapVa
|
||||
}
|
||||
|
||||
func sortToOrderString(sorts []model.Sort) string {
|
||||
strs := make([]string, 0)
|
||||
strs := make([]string, 0, len(sorts))
|
||||
for _, i := range sorts {
|
||||
str := i.Field
|
||||
if i.Direction != "" {
|
||||
@@ -110,17 +110,7 @@ func sortToOrderString(sorts []model.Sort) string {
|
||||
func parseBoolValue(v string) []string {
|
||||
bVal := "0"
|
||||
switch strings.ToLower(v) {
|
||||
case "yes":
|
||||
fallthrough
|
||||
case "true":
|
||||
fallthrough
|
||||
case "on":
|
||||
fallthrough
|
||||
case "t":
|
||||
fallthrough
|
||||
case "1":
|
||||
fallthrough
|
||||
case "y":
|
||||
case "yes", "true", "on", "t", "1", "y":
|
||||
bVal = "1"
|
||||
}
|
||||
return []string{bVal}
|
||||
|
||||
@@ -31,6 +31,11 @@ func (m *Model) LoadByID(id uint) error {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// SetUserID sets the owner of this model
|
||||
func (m *Model) SetUserID(userID uint) {
|
||||
m.UserID = userID
|
||||
}
|
||||
|
||||
// Save will save this model to the DB
|
||||
func (m *Model) Save() error {
|
||||
if m.UserID == 0 {
|
||||
|
||||
@@ -226,9 +226,7 @@ func (s *testsuite) TestDelete() {
|
||||
assert.Equal(s.T(), "Unable to delete a new object", err.Error())
|
||||
|
||||
m2 := Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
Name: "John Doe",
|
||||
}
|
||||
err2 := m2.Delete()
|
||||
@@ -442,9 +440,7 @@ func (s *testsuite) TestSaveCapabilitiesInvalid() {
|
||||
|
||||
// Empty model returns error
|
||||
m := Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
Capabilities: []string{"doesnotexist", "hosts.manage"},
|
||||
}
|
||||
err := m.SaveCapabilities()
|
||||
|
||||
@@ -89,16 +89,16 @@ func List(pageInfo model.PageInfo, filters []model.Filter, expand []string) (ent
|
||||
// DeleteAll will do just that, and should only be used for testing purposes.
|
||||
func DeleteAll() error {
|
||||
db := database.GetDB()
|
||||
if result := db.Exec(
|
||||
result := db.Exec(
|
||||
fmt.Sprintf(`DELETE FROM %s WHERE is_system = ?`, database.QuoteTableName("user")),
|
||||
false,
|
||||
); result.Error != nil {
|
||||
)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
if result := db.Exec(
|
||||
fmt.Sprintf(`DELETE FROM %s`, database.QuoteTableName("auth")),
|
||||
); result.Error != nil {
|
||||
result = db.Exec(fmt.Sprintf(`DELETE FROM %s`, database.QuoteTableName("auth")))
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
|
||||
@@ -33,17 +33,14 @@ func TestShutdown(t *testing.T) {
|
||||
err := Shutdown()
|
||||
require.Nil(t, err, "Shutdown should not return an error when jobqueue is started")
|
||||
|
||||
// nolint: gosimple
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
switch ctx.Err() {
|
||||
case context.DeadlineExceeded:
|
||||
fmt.Println("context timeout exceeded")
|
||||
case context.Canceled:
|
||||
fmt.Println("context cancelled by force. whole process is complete")
|
||||
default:
|
||||
require.Nil(t, ctx.Err(), "Context done state has unexpected value")
|
||||
}
|
||||
<-ctx.Done()
|
||||
switch ctx.Err() {
|
||||
case context.DeadlineExceeded:
|
||||
fmt.Println("context timeout exceeded")
|
||||
case context.Canceled:
|
||||
fmt.Println("context cancelled by force. whole process is complete")
|
||||
default:
|
||||
require.Nil(t, ctx.Err(), "Context done state has unexpected value")
|
||||
}
|
||||
|
||||
require.Nil(t, cancel, "Cancel function should be nil after Shutdown")
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/model"
|
||||
"npm/internal/test"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
@@ -183,9 +182,7 @@ func (s *testsuite) TestGetPrivateKey() {
|
||||
|
||||
// Set currentKeys and try again
|
||||
currentKeys = KeysModel{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
PrivateKey: s.privateKeyString,
|
||||
PublicKey: s.publicKeyString,
|
||||
}
|
||||
@@ -210,9 +207,7 @@ func (s *testsuite) TestGetPublicKey() {
|
||||
|
||||
// Set currentKeys and try again
|
||||
currentKeys = KeysModel{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
PrivateKey: s.privateKeyString,
|
||||
PublicKey: s.publicKeyString,
|
||||
}
|
||||
@@ -228,17 +223,13 @@ func (s *testsuite) TestGenerate() {
|
||||
defer goleak.VerifyNone(s.T(), goleak.IgnoreAnyFunction("database/sql.(*DB).connectionOpener"))
|
||||
|
||||
currentKeys = KeysModel{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
PrivateKey: s.privateKeyString,
|
||||
PublicKey: s.publicKeyString,
|
||||
}
|
||||
|
||||
usr := user.Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
}
|
||||
|
||||
// test 1, user key
|
||||
|
||||
@@ -168,7 +168,6 @@ func TestConfigure(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := Configure(tt.args.c); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Configure() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/certificateauthority"
|
||||
"npm/internal/entity/nginxtemplate"
|
||||
"npm/internal/entity/setting"
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/model"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const proxyTemplate = `# ------------------------------------------------------------
|
||||
{{#each Host.DomainNames}}
|
||||
# {{this}}
|
||||
{{/each}}
|
||||
# ------------------------------------------------------------
|
||||
|
||||
server {
|
||||
{{#if Config.Ipv4}}
|
||||
listen 80;
|
||||
{{/if}}
|
||||
{{#if Config.Ipv6}}
|
||||
listen [::]:80;
|
||||
{{/if}}
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
{{#if Config.Ipv4}}
|
||||
listen 443 ssl {{#if Host.HTTP2Support}}http2{{/if}};
|
||||
{{/if}}
|
||||
{{#if Config.Ipv6}}
|
||||
listen [::]:443 ssl {{#if Host.HTTP2Support}}http2{{/if}};
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
server_name {{#each Host.DomainNames}}{{this}} {{/each}};
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
include conf.d/include/ssl-ciphers.conf;
|
||||
{{#if Certificate.IsAcme}}
|
||||
ssl_certificate {{Certificate.Folder}}/fullchain.pem;
|
||||
ssl_certificate_key {{Certificate.Folder}}/privkey.pem;
|
||||
{{else}}
|
||||
# Custom SSL
|
||||
ssl_certificate /data/custom_ssl/npm-{{Certicicate.ID}}/fullchain.pem;
|
||||
ssl_certificate_key /data/custom_ssl/npm-{{Certificate.ID}}/privkey.pem;
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.CachingEnabled}}
|
||||
include conf.d/include/assets.conf;
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.BlockExploits}}
|
||||
include conf.d/include/block-exploits.conf;
|
||||
{{/if}}
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
{{#if Host.SSLForced}}
|
||||
{{#if Host.HSTSEnabled}}
|
||||
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
|
||||
add_header Strict-Transport-Security "max-age=63072000;{{#if Host.HSTSSubdomains}} includeSubDomains;{{/if}} preload" always;
|
||||
{{/if}}
|
||||
# Force SSL
|
||||
include conf.d/include/force-ssl.conf;
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.AllowWebsocketUpgrade}}
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
proxy_http_version 1.1;
|
||||
{{/if}}
|
||||
|
||||
access_log /data/logs/host-{{Host.ID}}_access.log proxy;
|
||||
error_log /data/logs/host-{{Host.ID}}_error.log warn;
|
||||
|
||||
{{Host.AdvancedConfig}}
|
||||
|
||||
# locations ?
|
||||
|
||||
# default location:
|
||||
location / {
|
||||
{{#if Host.AccessListID}}
|
||||
# Authorization
|
||||
auth_basic "Authorization required";
|
||||
auth_basic_user_file /data/access/{{Host.AccessListID}};
|
||||
# access_list.passauth ? todo
|
||||
{{/if}}
|
||||
|
||||
# Access Rules ? todo
|
||||
|
||||
# Access checks must...? todo
|
||||
|
||||
{{#if Certificate.ID}}
|
||||
{{#if Host.SSLForced}}
|
||||
{{#if Host.HSTSEnabled}}
|
||||
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
|
||||
add_header Strict-Transport-Security "max-age=63072000;{{#if Host.HSTSSubdomains}} includeSubDomains;{{/if}} preload" always;
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if Host.AllowWebsocketUpgrade}}
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
{{/if}}
|
||||
|
||||
# Proxy!
|
||||
add_header X-Served-By $host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
{{#if Upstream.ID}}
|
||||
# upstream
|
||||
proxy_pass {{Host.ProxyScheme}}://npm_upstream_{{Upstream.ID}};
|
||||
{{else}}
|
||||
# proxy a single host
|
||||
proxy_pass {{Host.ProxyScheme}}://{{Host.ProxyHost}}:{{Host.ProxyPort}};
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
# Legacy Custom Configuration
|
||||
include /data/nginx/custom/server_proxy[.]conf;
|
||||
}
|
||||
`
|
||||
|
||||
const upstreamTemplate = `# ------------------------------------------------------------
|
||||
# Upstream {{Upstream.ID}}: {{Upstream.Name}}
|
||||
# ------------------------------------------------------------
|
||||
|
||||
upstream npm_upstream_{{Upstream.ID}} {
|
||||
|
||||
{{#if Upstream.IPHash~}}
|
||||
ip_hash;
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.NTLM~}}
|
||||
ntlm;
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.Keepalive~}}
|
||||
keepalive {{Upstream.Keepalive}};
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.KeepaliveRequests~}}
|
||||
keepalive_requests {{Upstream.KeepaliveRequests}};
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.KeepaliveTime~}}
|
||||
keepalive_time {{Upstream.KeepaliveTime}};
|
||||
{{~/if}}
|
||||
|
||||
{{#if Upstream.KeepaliveTimeout~}}
|
||||
keepalive_timeout {{Upstream.KeepaliveTimeout}};
|
||||
{{~/if}}
|
||||
|
||||
{{Upstream.AdvancedConfig}}
|
||||
|
||||
{{#each Upstream.Servers~}}
|
||||
{{#unless IsDeleted~}}
|
||||
server {{Server}} {{#if Weight}}weight={{Weight}} {{/if}}{{#if MaxConns}}max_conns={{MaxConns}} {{/if}}{{#if MaxFails}}max_fails={{MaxFails}} {{/if}}{{#if FailTimeout}}fail_timeout={{FailTimeout}} {{/if}}{{#if Backup}}backup{{/if}};
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
}
|
||||
`
|
||||
|
||||
// initialData seeds the tables created by initialSchema with the same
|
||||
// default rows that the old per-engine dbmate migrations inserted:
|
||||
// capabilities, default settings, built-in certificate authorities, the
|
||||
// system user, and the built-in nginx templates.
|
||||
func initialData() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20201013035839",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
now := time.Now().UnixMilli()
|
||||
base := func() model.Base {
|
||||
return model.Base{CreatedAt: now, UpdatedAt: now}
|
||||
}
|
||||
|
||||
capabilities := []entity.Capability{
|
||||
{Name: user.CapabilityFullAdmin},
|
||||
{Name: user.CapabilityAccessListsView},
|
||||
{Name: user.CapabilityAccessListsManage},
|
||||
{Name: user.CapabilityAuditLogView},
|
||||
{Name: user.CapabilityCertificatesView},
|
||||
{Name: user.CapabilityCertificatesManage},
|
||||
{Name: user.CapabilityCertificateAuthoritiesView},
|
||||
{Name: user.CapabilityCertificateAuthoritiesManage},
|
||||
{Name: user.CapabilityDNSProvidersView},
|
||||
{Name: user.CapabilityDNSProvidersManage},
|
||||
{Name: user.CapabilityHostsView},
|
||||
{Name: user.CapabilityHostsManage},
|
||||
{Name: user.CapabilityNginxTemplatesView},
|
||||
{Name: user.CapabilityNginxTemplatesManage},
|
||||
{Name: user.CapabilitySettingsManage},
|
||||
{Name: user.CapabilityStreamsView},
|
||||
{Name: user.CapabilityStreamsManage},
|
||||
{Name: user.CapabilityUsersManage},
|
||||
}
|
||||
if err := tx.Create(&capabilities).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings := []setting.Model{
|
||||
{
|
||||
Base: base(),
|
||||
Name: "default-site",
|
||||
Description: "What to show users who hit your Nginx server by default",
|
||||
Value: datatypes.JSON(`"welcome"`),
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "auth-methods",
|
||||
Description: "Which methods are enabled for authentication",
|
||||
Value: datatypes.JSON(`["local"]`),
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "oauth-auth",
|
||||
Description: "Configuration for OAuth authentication",
|
||||
Value: datatypes.JSON(`{}`),
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "ldap-auth",
|
||||
Description: "Configuration for LDAP authentication",
|
||||
Value: datatypes.JSON(`{"host": "", "dn": "", "sync_by": "uid"}`),
|
||||
},
|
||||
}
|
||||
if err := tx.Create(&settings).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certificateAuthorities := []certificateauthority.Model{
|
||||
{
|
||||
Base: base(),
|
||||
Name: "ZeroSSL",
|
||||
AcmeshServer: "zerossl",
|
||||
IsWildcardSupported: true,
|
||||
MaxDomains: 10,
|
||||
IsReadonly: true,
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "Let's Encrypt",
|
||||
AcmeshServer: "https://acme-v02.api.letsencrypt.org/directory",
|
||||
IsWildcardSupported: true,
|
||||
MaxDomains: 10,
|
||||
IsReadonly: true,
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "Buypass Go SSL",
|
||||
AcmeshServer: "https://api.buypass.com/acme/directory",
|
||||
IsWildcardSupported: false,
|
||||
MaxDomains: 5,
|
||||
IsReadonly: true,
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "SSL.com",
|
||||
AcmeshServer: "ssl.com",
|
||||
IsWildcardSupported: false,
|
||||
MaxDomains: 10,
|
||||
IsReadonly: true,
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "Let's Encrypt (Testing)",
|
||||
AcmeshServer: "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
IsWildcardSupported: true,
|
||||
MaxDomains: 10,
|
||||
IsReadonly: true,
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
Name: "Buypass Go SSL (Testing)",
|
||||
AcmeshServer: "https://api.test4.buypass.no/acme/directory",
|
||||
IsWildcardSupported: false,
|
||||
MaxDomains: 5,
|
||||
IsReadonly: true,
|
||||
},
|
||||
}
|
||||
if err := tx.Create(&certificateAuthorities).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
systemUser := user.Model{
|
||||
Base: base(),
|
||||
Name: "System",
|
||||
Email: "system@localhost",
|
||||
IsSystem: true,
|
||||
}
|
||||
if err := tx.Create(&systemUser).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
templates := []nginxtemplate.Model{
|
||||
{
|
||||
Base: base(),
|
||||
UserID: systemUser.ID,
|
||||
Name: "Default Proxy Template",
|
||||
Type: "proxy",
|
||||
Template: proxyTemplate,
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
UserID: systemUser.ID,
|
||||
Name: "Default Redirect Template",
|
||||
Type: "redirect",
|
||||
Template: "# this is a redirect template",
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
UserID: systemUser.ID,
|
||||
Name: "Default Dead Template",
|
||||
Type: "dead",
|
||||
Template: "# this is a dead template",
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
UserID: systemUser.ID,
|
||||
Name: "Default Stream Template",
|
||||
Type: "stream",
|
||||
Template: "# this is a stream template",
|
||||
},
|
||||
{
|
||||
Base: base(),
|
||||
UserID: systemUser.ID,
|
||||
Name: "Default Upstream Template",
|
||||
Type: "upstream",
|
||||
Template: upstreamTemplate,
|
||||
},
|
||||
}
|
||||
return tx.Create(&templates).Error
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"npm/internal/logger"
|
||||
)
|
||||
|
||||
// logHandler routes polyschema's slog output through the application
|
||||
// logger, so migration progress and warnings (e.g. MySQL committing DDL
|
||||
// implicitly) show up alongside everything else.
|
||||
type logHandler struct {
|
||||
attrs []slog.Attr
|
||||
}
|
||||
|
||||
func newSlogLogger() *slog.Logger {
|
||||
return slog.New(&logHandler{})
|
||||
}
|
||||
|
||||
// Enabled lets every record through; the application logger applies its
|
||||
// own level filtering.
|
||||
func (*logHandler) Enabled(context.Context, slog.Level) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *logHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
var b strings.Builder
|
||||
b.WriteString(r.Message)
|
||||
write := func(a slog.Attr) bool {
|
||||
fmt.Fprintf(&b, " %s=%v", a.Key, a.Value)
|
||||
return true
|
||||
}
|
||||
for _, a := range h.attrs {
|
||||
write(a)
|
||||
}
|
||||
r.Attrs(write)
|
||||
|
||||
msg := b.String()
|
||||
switch {
|
||||
case r.Level >= slog.LevelWarn:
|
||||
logger.Warn("%s", msg)
|
||||
case r.Level >= slog.LevelInfo:
|
||||
logger.Info("%s", msg)
|
||||
default:
|
||||
logger.Debug("%s", msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *logHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return &logHandler{attrs: slices.Concat(h.attrs, attrs)}
|
||||
}
|
||||
|
||||
// WithGroup is unused by polyschema, so groups are flattened.
|
||||
func (h *logHandler) WithGroup(string) slog.Handler {
|
||||
return h
|
||||
}
|
||||
@@ -1,58 +1,49 @@
|
||||
// Package migrations owns the application's database schema. It replaces
|
||||
// the previous dbmate-based setup: instead of hand-written, per-engine SQL
|
||||
// files, each migration is a small Go function driven by gorm, so a single
|
||||
// migration works against Postgres, MySQL and SQLite alike.
|
||||
// Package migrations owns the application's database schema and seed data.
|
||||
//
|
||||
// Both live as portable polyschema migration files in embed/migrations/
|
||||
// (see https://github.com/jc21/polyschema): one YAML file per change,
|
||||
// rendered into the right SQL for Postgres, MySQL or SQLite at runtime.
|
||||
// Applied files are recorded in the historyTable, so on every boot only
|
||||
// pending files run.
|
||||
//
|
||||
// Applied files must never be edited: polyschema checksums them and
|
||||
// refuses to start if one changes. Schema changes are made by adding a
|
||||
// new file with a higher version number.
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
||||
"npm/embed"
|
||||
"npm/internal/config"
|
||||
"npm/internal/database"
|
||||
"npm/internal/logger"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
polyschema "github.com/jc21/polyschema/v1"
|
||||
)
|
||||
|
||||
// legacyMigrationsTable is the table dbmate used to track applied
|
||||
// migrations. Its presence indicates a database that was created before
|
||||
// the switch to gormigrate.
|
||||
const legacyMigrationsTable = "schema_migrations"
|
||||
|
||||
// legacyMigrationIDs maps the dbmate migration versions this application
|
||||
// used to ship to the gormigrate migrations that replace them, in order.
|
||||
// Existing installs already have this schema and data applied via dbmate,
|
||||
// so on first boot after upgrading we mark them as already run rather
|
||||
// than re-running them.
|
||||
var legacyMigrationIDs = []string{
|
||||
"20201013035318", // initial_schema
|
||||
"20201013035839", // initial_data
|
||||
}
|
||||
|
||||
// all is the ordered list of every migration this application has ever
|
||||
// shipped. Entries must never be reordered or edited once released;
|
||||
// schema changes are made by appending a new migration.
|
||||
func all() []*gormigrate.Migration {
|
||||
return []*gormigrate.Migration{
|
||||
initialSchema(),
|
||||
initialData(),
|
||||
}
|
||||
}
|
||||
// historyTable tracks which migration files have been applied.
|
||||
const historyTable = "migrations"
|
||||
|
||||
type afterMigrationComplete func()
|
||||
|
||||
// Migrate will bring the db up to date
|
||||
// Migrate applies any pending migrations, then calls followup on success.
|
||||
func Migrate(followup afterMigrationComplete) bool {
|
||||
db := database.GetDB()
|
||||
if db == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if err := bootstrapFromLegacyDBMate(db); err != nil {
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
logger.Error("MigrationError", err)
|
||||
return false
|
||||
}
|
||||
|
||||
m := gormigrate.New(db, gormigrate.DefaultOptions, all())
|
||||
if err := m.Migrate(); err != nil {
|
||||
if err := migrate(sqlDB); err != nil {
|
||||
logger.Error("MigrationError", err)
|
||||
return false
|
||||
}
|
||||
@@ -63,45 +54,23 @@ func Migrate(followup afterMigrationComplete) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// bootstrapFromLegacyDBMate detects a database that was migrated by the
|
||||
// old dbmate setup and pre-marks the gormigrate migrations that replace
|
||||
// dbmate's initial_schema/initial_data as already applied, so gormigrate
|
||||
// doesn't try to recreate tables or re-insert seed data that already
|
||||
// exist. It's a no-op for a brand new database and for one that has
|
||||
// already been bootstrapped.
|
||||
func bootstrapFromLegacyDBMate(db *gorm.DB) error {
|
||||
if !db.Migrator().HasTable(legacyMigrationsTable) {
|
||||
// Fresh install, nothing to bootstrap.
|
||||
return nil
|
||||
// migrate applies every pending file in embed/migrations/ for the
|
||||
// configured database engine.
|
||||
func migrate(sqlDB *sql.DB) error {
|
||||
engine := config.Configuration.DB.GetDriver()
|
||||
dialect := polyschema.DialectByName(engine)
|
||||
if dialect == nil {
|
||||
return fmt.Errorf("migrations: unsupported database driver %q", engine)
|
||||
}
|
||||
|
||||
var legacyCount int64
|
||||
if err := db.Table(legacyMigrationsTable).Count(&legacyCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if legacyCount == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if db.Migrator().HasTable(gormigrate.DefaultOptions.TableName) {
|
||||
// Already bootstrapped (or running fresh under gormigrate already).
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info("Existing dbmate-managed database detected, marking bootstrap migrations as applied")
|
||||
|
||||
type migrationRecord struct {
|
||||
ID string `gorm:"primaryKey;column:id;size:255"`
|
||||
}
|
||||
if err := db.Table(gormigrate.DefaultOptions.TableName).AutoMigrate(&migrationRecord{}); err != nil {
|
||||
files, err := fs.Sub(embed.MigrationFiles, "migrations")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, id := range legacyMigrationIDs {
|
||||
if err := db.Table(gormigrate.DefaultOptions.TableName).Create(&migrationRecord{ID: id}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
m := polyschema.New(sqlDB, dialect,
|
||||
polyschema.WithTable(historyTable),
|
||||
polyschema.WithLogger(newSlogLogger()),
|
||||
)
|
||||
return m.Up(context.Background(), files)
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"npm/embed"
|
||||
"npm/internal/config"
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/certificateauthority"
|
||||
@@ -12,16 +15,18 @@ import (
|
||||
"npm/internal/entity/user"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
polyschema "github.com/jc21/polyschema/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
gormlogger "gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func openTestDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
config.Configuration.DB.Driver = config.DatabaseSqlite
|
||||
dsn := filepath.Join(t.TempDir(), "test.db")
|
||||
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
||||
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{Logger: gormlogger.Default.LogMode(gormlogger.Silent)})
|
||||
require.NoError(t, err)
|
||||
return db
|
||||
}
|
||||
@@ -55,9 +60,9 @@ func TestMigrateFreshDatabase(t *testing.T) {
|
||||
require.NoError(t, db.Model(&nginxtemplate.Model{}).Count(&templateCount).Error)
|
||||
assert.EqualValues(t, 5, templateCount)
|
||||
|
||||
var migrationCount int64
|
||||
require.NoError(t, db.Table(gormigrate.DefaultOptions.TableName).Count(&migrationCount).Error)
|
||||
assert.EqualValues(t, len(all()), migrationCount)
|
||||
var historyCount int64
|
||||
require.NoError(t, db.Table(historyTable).Count(&historyCount).Error)
|
||||
assert.EqualValues(t, 2, historyCount, "one history row per migration file")
|
||||
}
|
||||
|
||||
func TestMigrateIsIdempotent(t *testing.T) {
|
||||
@@ -76,36 +81,26 @@ func TestMigrateIsIdempotent(t *testing.T) {
|
||||
assert.EqualValues(t, 1, userCount)
|
||||
}
|
||||
|
||||
// TestBootstrapFromLegacyDBMate simulates a database that was previously
|
||||
// migrated by dbmate: it has dbmate's schema_migrations table with the two
|
||||
// legacy versions recorded, and already contains seeded data. Migrate must
|
||||
// detect this and mark the equivalent gormigrate migrations as already
|
||||
// applied instead of re-running them and duplicating the seed data.
|
||||
func TestBootstrapFromLegacyDBMate(t *testing.T) {
|
||||
db := openTestDB(t)
|
||||
database.SetDB(db)
|
||||
|
||||
require.NoError(t, db.Exec(`CREATE TABLE schema_migrations (version TEXT PRIMARY KEY)`).Error)
|
||||
for _, v := range legacyMigrationIDs {
|
||||
require.NoError(t, db.Exec(`INSERT INTO schema_migrations (version) VALUES (?)`, v).Error)
|
||||
}
|
||||
require.NoError(t, db.Exec(`CREATE TABLE capability (name TEXT PRIMARY KEY)`).Error)
|
||||
require.NoError(t, db.Exec(`INSERT INTO capability (name) VALUES (?)`, "full-admin").Error)
|
||||
|
||||
require.True(t, Migrate(func() {}))
|
||||
|
||||
var capabilityCount int64
|
||||
require.NoError(t, db.Table("capability").Count(&capabilityCount).Error)
|
||||
assert.EqualValues(t, 1, capabilityCount, "legacy data must not be re-seeded")
|
||||
|
||||
var migrationIDs []string
|
||||
require.NoError(t, db.Table(gormigrate.DefaultOptions.TableName).Pluck("id", &migrationIDs).Error)
|
||||
assert.ElementsMatch(t, legacyMigrationIDs, migrationIDs)
|
||||
}
|
||||
|
||||
func TestMigrateNoDB(t *testing.T) {
|
||||
database.SetDB(nil)
|
||||
assert.False(t, Migrate(func() {
|
||||
t.Error("followup should not be called")
|
||||
}))
|
||||
}
|
||||
|
||||
// TestMigrationsRenderForEveryEngine catches a migration file that won't
|
||||
// run on one of the supported engines, without needing those databases.
|
||||
func TestMigrationsRenderForEveryEngine(t *testing.T) {
|
||||
files, err := fs.Sub(embed.MigrationFiles, "migrations")
|
||||
require.NoError(t, err)
|
||||
|
||||
issues, err := polyschema.CheckFS(files, polyschema.Postgres, polyschema.MySQL, polyschema.SQLite)
|
||||
require.NoError(t, err)
|
||||
for _, is := range issues {
|
||||
if is.Severity == polyschema.Error {
|
||||
t.Error(is)
|
||||
} else {
|
||||
t.Log(is)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/accesslist"
|
||||
"npm/internal/entity/auth"
|
||||
"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/upstreamserver"
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/jwt"
|
||||
"npm/internal/model"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// auditLogSchema mirrors the audit_log table. There is no application
|
||||
// entity for it yet, so its schema is only declared here for AutoMigrate.
|
||||
type auditLogSchema struct {
|
||||
model.Base
|
||||
UserID uint `gorm:"column:user_id"`
|
||||
ObjectType string `gorm:"column:object_type"`
|
||||
ObjectID uint `gorm:"column:object_id"`
|
||||
Action string `gorm:"column:action"`
|
||||
Meta string `gorm:"column:meta"`
|
||||
}
|
||||
|
||||
// TableName overrides the table name used by gorm
|
||||
func (auditLogSchema) TableName() string {
|
||||
return "audit_log"
|
||||
}
|
||||
|
||||
// initialSchema creates every table used by the application. It replaces
|
||||
// the hand-written, per-engine SQL that dbmate used to run: gorm's dialect
|
||||
// drivers already know how to translate these structs for Postgres, MySQL
|
||||
// and SQLite, so there's no need to maintain three copies of the same DDL.
|
||||
func initialSchema() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "20201013035318",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&jwt.KeysModel{},
|
||||
&user.Model{},
|
||||
&entity.Capability{},
|
||||
&user.HasCapabilityModel{},
|
||||
&auth.Model{},
|
||||
&setting.Model{},
|
||||
&auditLogSchema{},
|
||||
&certificateauthority.Model{},
|
||||
&dnsprovider.Model{},
|
||||
&certificate.Model{},
|
||||
&stream.Model{},
|
||||
&nginxtemplate.Model{},
|
||||
&upstream.Model{},
|
||||
&upstreamserver.Model{},
|
||||
&accesslist.Model{},
|
||||
&host.Model{},
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"npm/internal/entity/host"
|
||||
"npm/internal/model"
|
||||
"npm/internal/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -25,9 +24,7 @@ func TestGetHostFilename(t *testing.T) {
|
||||
{
|
||||
"test1",
|
||||
host.Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
},
|
||||
"",
|
||||
"/data/nginx/hosts/host_10.conf",
|
||||
@@ -35,9 +32,7 @@ func TestGetHostFilename(t *testing.T) {
|
||||
{
|
||||
"test2",
|
||||
host.Model{
|
||||
Base: model.Base{
|
||||
ID: 10,
|
||||
},
|
||||
ID: 10,
|
||||
},
|
||||
".deleted",
|
||||
"/data/nginx/hosts/host_10.conf.deleted",
|
||||
|
||||
@@ -50,10 +50,8 @@ func GetFilterMap(m any, globalTablePrefix string) map[string]model.FilterMapVal
|
||||
}
|
||||
|
||||
// Iterate over all available fields and read the tag value
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
// Get the field, returns https://golang.org/pkg/reflect/#StructField
|
||||
field := t.Field(i)
|
||||
|
||||
// Each field is a https://golang.org/pkg/reflect/#StructField
|
||||
for field := range t.Fields() {
|
||||
// Get the field tag value
|
||||
filterTag := field.Tag.Get("filter")
|
||||
dbTag := field.Tag.Get("gorm")
|
||||
@@ -61,10 +59,9 @@ func GetFilterMap(m any, globalTablePrefix string) map[string]model.FilterMapVal
|
||||
// Filter -> Schema mapping
|
||||
if filterTag != "" && filterTag != "-" {
|
||||
f := model.FilterMapValue{
|
||||
Model: name,
|
||||
Model: name,
|
||||
Schema: getFilterTagSchema(filterTag),
|
||||
}
|
||||
|
||||
f.Schema = getFilterTagSchema(filterTag)
|
||||
parts := strings.Split(filterTag, ",")
|
||||
|
||||
// Filter -> DB Field mapping
|
||||
@@ -104,15 +101,9 @@ func getFilterTagSchema(filterTag string) string {
|
||||
}
|
||||
|
||||
switch items[1] {
|
||||
case "number":
|
||||
fallthrough
|
||||
case "int":
|
||||
fallthrough
|
||||
case "integer":
|
||||
case "number", "int", "integer":
|
||||
return intFieldSchema(items[0])
|
||||
case "bool":
|
||||
fallthrough
|
||||
case "boolean":
|
||||
case "bool", "boolean":
|
||||
return boolFieldSchema(items[0])
|
||||
case "date":
|
||||
return dateFieldSchema(items[0])
|
||||
@@ -131,7 +122,7 @@ func getFilterTagSchema(filterTag string) string {
|
||||
// object given and by reading the struct "filter" tags.
|
||||
func GetFilterSchema(m any) string {
|
||||
filterMap := GetFilterMap(m, "")
|
||||
schemas := make([]string, 0)
|
||||
schemas := make([]string, 0, len(filterMap))
|
||||
|
||||
for _, f := range filterMap {
|
||||
schemas = append(schemas, f.Schema)
|
||||
|
||||
@@ -25,7 +25,10 @@ func TestDBDate_Value(t *testing.T) {
|
||||
}
|
||||
|
||||
// Convert the value to int64
|
||||
unixTime := value.(int64)
|
||||
unixTime, ok := value.(int64)
|
||||
if !ok {
|
||||
t.Fatalf("Expected int64 value, got %T", value)
|
||||
}
|
||||
|
||||
// Convert the unix time back to time.Time
|
||||
actualTime := time.Unix(unixTime, 0)
|
||||
@@ -55,11 +58,11 @@ func TestDBDate_Scan(t *testing.T) {
|
||||
}
|
||||
|
||||
// Convert the DBDate's time to unix timestamp for comparison
|
||||
actualUnixTime := dbDate.Time.Unix()
|
||||
actualUnixSec := dbDate.Time.Unix()
|
||||
|
||||
// Compare the actual unix time with the expected unix time
|
||||
if actualUnixTime != unixTime {
|
||||
t.Errorf("Expected unix time '%v', got '%v'", unixTime, actualUnixTime)
|
||||
if actualUnixSec != unixTime {
|
||||
t.Errorf("Expected unix time '%v', got '%v'", unixTime, actualUnixSec)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +85,12 @@ func TestDBDate_UnmarshalJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
// Convert the DBDate's time to unix timestamp for comparison
|
||||
actualUnixTime := dbDate.Time.Unix()
|
||||
actualUnixSec := dbDate.Time.Unix()
|
||||
|
||||
// Compare the actual unix time with the expected unix time
|
||||
expectedUnixTime := int64(1640995200)
|
||||
if actualUnixTime != expectedUnixTime {
|
||||
t.Errorf("Expected unix time '%v', got '%v'", expectedUnixTime, actualUnixTime)
|
||||
if actualUnixSec != expectedUnixTime {
|
||||
t.Errorf("Expected unix time '%v', got '%v'", expectedUnixTime, actualUnixSec)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ func (j *JSONB) Scan(src any) error {
|
||||
var srcString string
|
||||
switch v := src.(type) {
|
||||
case string:
|
||||
srcString = src.(string)
|
||||
srcString = v
|
||||
case []uint8:
|
||||
srcString = string(src.([]uint8))
|
||||
srcString = string(v)
|
||||
default:
|
||||
return eris.Errorf("Incompatible type for JSONB: %v", v)
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ func TestNullableDBDateValue(t *testing.T) {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expectedValue := tme.Unix()
|
||||
expectedSec := tme.Unix()
|
||||
|
||||
if value != expectedValue {
|
||||
t.Errorf("Incorrect value. Expected: %d, Got: %v", expectedValue, value)
|
||||
if value != expectedSec {
|
||||
t.Errorf("Incorrect value. Expected: %d, Got: %v", expectedSec, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,10 +101,10 @@ func TestNullableDBDateAsInt64(t *testing.T) {
|
||||
}
|
||||
|
||||
unixtime := d.AsInt64()
|
||||
expectedUnixtime := tme.Unix()
|
||||
expectedSec := tme.Unix()
|
||||
|
||||
if unixtime != expectedUnixtime {
|
||||
t.Errorf("Incorrect unixtime. Expected: %d, Got: %d", expectedUnixtime, unixtime)
|
||||
if unixtime != expectedSec {
|
||||
t.Errorf("Incorrect unixtime. Expected: %d, Got: %d", expectedSec, unixtime)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SliceContainsItem returns whether the slice given contains the item given
|
||||
func SliceContainsItem(slice []string, item string) bool {
|
||||
for _, a := range slice {
|
||||
if a == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(slice, item)
|
||||
}
|
||||
|
||||
// SliceContainsInt returns whether the slice given contains the item given
|
||||
func SliceContainsInt(slice []int, item int) bool {
|
||||
for _, a := range slice {
|
||||
if a == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(slice, item)
|
||||
}
|
||||
|
||||
// ConvertIntSliceToString returns a comma separated string of all items in the slice
|
||||
func ConvertIntSliceToString(slice []int) string {
|
||||
strs := []string{}
|
||||
strs := make([]string, 0, len(slice))
|
||||
for _, item := range slice {
|
||||
strs = append(strs, strconv.Itoa(item))
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ type MockCertificate struct {
|
||||
|
||||
func (m *MockCertificate) GetByID(id uint) (certificate.Model, error) {
|
||||
args := m.Called(id)
|
||||
return args.Get(0).(certificate.Model), args.Error(1)
|
||||
model, _ := args.Get(0).(certificate.Model)
|
||||
return model, args.Error(1)
|
||||
}
|
||||
|
||||
type MockUpstream struct {
|
||||
@@ -30,7 +31,8 @@ type MockUpstream struct {
|
||||
|
||||
func (m *MockUpstream) GetByID(id uint) (upstream.Model, error) {
|
||||
args := m.Called(id)
|
||||
return args.Get(0).(upstream.Model), args.Error(1)
|
||||
model, _ := args.Get(0).(upstream.Model)
|
||||
return model, args.Error(1)
|
||||
}
|
||||
|
||||
type MockNginxTemplate struct {
|
||||
@@ -39,7 +41,8 @@ type MockNginxTemplate struct {
|
||||
|
||||
func (m *MockNginxTemplate) GetByID(id uint) (nginxtemplate.Model, error) {
|
||||
args := m.Called(id)
|
||||
return args.Get(0).(nginxtemplate.Model), args.Error(1)
|
||||
model, _ := args.Get(0).(nginxtemplate.Model)
|
||||
return model, args.Error(1)
|
||||
}
|
||||
|
||||
func TestValidateHost(t *testing.T) {
|
||||
|
||||
+24
-14
@@ -1,22 +1,32 @@
|
||||
#!/bin/bash
|
||||
set -eufo pipefail
|
||||
|
||||
BLUE='\E[1;34m'
|
||||
YELLOW='\E[1;33m'
|
||||
RESET='\E[0m'
|
||||
RESULT=0
|
||||
export RED='\E[1;31m'
|
||||
export YELLOW='\E[1;33m'
|
||||
export RESET='\033[0m'
|
||||
|
||||
# go files: incomplete comment check
|
||||
INCOMPLETE_COMMENTS=$(find . -iname "*.go*" | grep -v " " | xargs grep --colour -H -n -E "^\s*\/\/\s*[A-Z]\w+ \.{3}" 2>/dev/null)
|
||||
if [[ -n "$INCOMPLETE_COMMENTS" ]]; then
|
||||
echo -e "${BLUE}❯ ${YELLOW}WARN: Please fix incomplete exported comments:${RESET}"
|
||||
echo -e "${RED}${INCOMPLETE_COMMENTS}${RESET}"
|
||||
echo
|
||||
# RESULT=1
|
||||
PROJECT_DIR="$(cd -- "$(dirname -- "$0")/.." && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
trap cleanup EXIT
|
||||
cleanup() {
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo -e "${RED}LINTING FAILED${RESET}"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! command -v golangci-lint &>/dev/null; then
|
||||
echo -e "${YELLOW}Installing golangci-lint ...${RESET}"
|
||||
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}golangci-lint ...${RESET}"
|
||||
if ! golangci-lint run -E goimports ./...; then
|
||||
exit 1
|
||||
golangci-lint --max-same-issues=0 --max-issues-per-linter=0 run ./...
|
||||
|
||||
if ! command -v modernize &>/dev/null; then
|
||||
echo -e "${YELLOW}Installing modernize ...${RESET}"
|
||||
go install golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest
|
||||
fi
|
||||
|
||||
exit "$RESULT"
|
||||
echo -e "${YELLOW}modernize ...${RESET}"
|
||||
modernize -test "$@" ./...
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
if ! command -v go-test-coverage &>/dev/null; then
|
||||
go install github.com/vladopajic/go-test-coverage/v2@latest
|
||||
fi
|
||||
if ! command -v tparse &>/dev/null; then
|
||||
go install github.com/mfridman/tparse@latest
|
||||
fi
|
||||
|
||||
rm -f "$DIR/coverage.html"
|
||||
|
||||
trap cleanup EXIT
|
||||
cleanup() {
|
||||
rm -f "$DIR/coverage.out"
|
||||
}
|
||||
|
||||
echo "Running go test suite ..."
|
||||
go test -json -cover ./... -coverprofile="$DIR/coverage.out" | tparse
|
||||
go tool cover -html="$DIR/coverage.out" -o "$DIR/coverage.html"
|
||||
go-test-coverage -c "$DIR/.testcoverage.yml"
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
export RED='\E[1;31m'
|
||||
export YELLOW='\E[1;33m'
|
||||
export RESET='\033[0m'
|
||||
|
||||
cd -- "$(dirname -- "$0")/.." || exit 1
|
||||
|
||||
if ! command -v go-test-coverage &>/dev/null; then
|
||||
echo -e "${YELLOW}Installing go-test-coverage ...${RESET}"
|
||||
go install github.com/vladopajic/go-test-coverage/v2@latest
|
||||
fi
|
||||
|
||||
if ! command -v tparse &>/dev/null; then
|
||||
echo -e "${YELLOW}Installing tparse ...${RESET}"
|
||||
go install github.com/mfridman/tparse@latest
|
||||
fi
|
||||
|
||||
if ! command -v go-junit-report &>/dev/null; then
|
||||
echo -e "${YELLOW}Installing go-junit-report ...${RESET}"
|
||||
go install github.com/jstemmer/go-junit-report/v2@latest
|
||||
fi
|
||||
|
||||
trap cleanup EXIT
|
||||
cleanup() {
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo -e "${RED}UNIT TESTING FAILED - check output and consider minimum coverage requirements${RESET}"
|
||||
fi
|
||||
rm -f coverage.out
|
||||
}
|
||||
|
||||
mkdir -p ../test/results/html-reports ../test/results/junit
|
||||
go test -json -cover -coverprofile="./coverage.out" ./... | tparse || true
|
||||
go tool cover -html="./coverage.out" -o "../test/results/html-reports/backend-coverage.html"
|
||||
go test -v -covermode=atomic ./... 2>&1 | go-junit-report >../test/results/junit/backend.xml
|
||||
|
||||
# this enforces minimum coverage requirements
|
||||
go-test-coverage -c .testcoverage.yml
|
||||
Reference in new issue
Block a user