mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-13 17:24:44 +08:00
feat: 增加nginx config 格式化工具
This commit is contained in:
parent
7dad47464d
commit
a1ac689a5e
@ -99,3 +99,8 @@ func ExistWithStrArray(str string, arr []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsNum(s string) bool {
|
||||
_, err := strconv.ParseFloat(s, 64)
|
||||
return err == nil
|
||||
}
|
||||
|
50
backend/utils/nginx/components/block.go
Normal file
50
backend/utils/nginx/components/block.go
Normal file
@ -0,0 +1,50 @@
|
||||
package components
|
||||
|
||||
type Block struct {
|
||||
Comment string
|
||||
Directives []IDirective
|
||||
}
|
||||
|
||||
func (b *Block) GetDirectives() []IDirective {
|
||||
return b.Directives
|
||||
}
|
||||
|
||||
func (b *Block) GetComment() string {
|
||||
return b.Comment
|
||||
}
|
||||
|
||||
func (b *Block) FindDirectives(directiveName string) []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, directive := range b.GetDirectives() {
|
||||
if directive.GetName() == directiveName {
|
||||
directives = append(directives, directive)
|
||||
}
|
||||
if directive.GetBlock() != nil {
|
||||
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
|
||||
}
|
||||
}
|
||||
|
||||
return directives
|
||||
}
|
||||
|
||||
func (b *Block) UpdateDirectives(directiveName string, directive Directive) {
|
||||
directives := make([]IDirective, len(b.GetDirectives()))
|
||||
index := -1
|
||||
for i, dir := range b.GetDirectives() {
|
||||
if dir.GetName() == directiveName {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index > -1 {
|
||||
directives[index] = &directive
|
||||
} else {
|
||||
directives = append(directives, &directive)
|
||||
}
|
||||
b.Directives = directives
|
||||
}
|
||||
|
||||
func (b *Block) AddDirectives(directive Directive) {
|
||||
directives := append(b.GetDirectives(), &directive)
|
||||
b.Directives = directives
|
||||
}
|
21
backend/utils/nginx/components/comment.go
Normal file
21
backend/utils/nginx/components/comment.go
Normal file
@ -0,0 +1,21 @@
|
||||
package components
|
||||
|
||||
type Comment struct {
|
||||
Detail string
|
||||
}
|
||||
|
||||
func (c *Comment) GetName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Comment) GetParameters() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (c *Comment) GetBlock() IBlock {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Comment) GetComment() string {
|
||||
return c.Detail
|
||||
}
|
28
backend/utils/nginx/components/config.go
Normal file
28
backend/utils/nginx/components/config.go
Normal file
@ -0,0 +1,28 @@
|
||||
package components
|
||||
|
||||
type Config struct {
|
||||
*Block
|
||||
FilePath string
|
||||
}
|
||||
|
||||
func (c *Config) FindDirectives(directiveName string) []IDirective {
|
||||
return c.Block.FindDirectives(directiveName)
|
||||
}
|
||||
|
||||
func (c *Config) FindUpstreams() []*Upstream {
|
||||
var upstreams []*Upstream
|
||||
directives := c.Block.FindDirectives("upstream")
|
||||
for _, directive := range directives {
|
||||
upstreams = append(upstreams, directive.(*Upstream))
|
||||
}
|
||||
return upstreams
|
||||
}
|
||||
|
||||
func (c *Config) FindServers() []*Server {
|
||||
var servers []*Server
|
||||
directives := c.Block.FindDirectives("server")
|
||||
for _, directive := range directives {
|
||||
servers = append(servers, directive.(*Server))
|
||||
}
|
||||
return servers
|
||||
}
|
24
backend/utils/nginx/components/directive.go
Normal file
24
backend/utils/nginx/components/directive.go
Normal file
@ -0,0 +1,24 @@
|
||||
package components
|
||||
|
||||
type Directive struct {
|
||||
Block IBlock
|
||||
Name string
|
||||
Comment string
|
||||
Parameters []string
|
||||
}
|
||||
|
||||
func (d *Directive) GetComment() string {
|
||||
return d.Comment
|
||||
}
|
||||
|
||||
func (d *Directive) GetName() string {
|
||||
return d.Name
|
||||
}
|
||||
|
||||
func (d *Directive) GetParameters() []string {
|
||||
return d.Parameters
|
||||
}
|
||||
|
||||
func (d *Directive) GetBlock() IBlock {
|
||||
return d.Block
|
||||
}
|
93
backend/utils/nginx/components/http.go
Normal file
93
backend/utils/nginx/components/http.go
Normal file
@ -0,0 +1,93 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Http struct {
|
||||
Comment string
|
||||
Servers []*Server
|
||||
Directives []IDirective
|
||||
}
|
||||
|
||||
func (h *Http) GetComment() string {
|
||||
return h.Comment
|
||||
}
|
||||
|
||||
func NewHttp(directive IDirective) (*Http, error) {
|
||||
if block := directive.GetBlock(); block != nil {
|
||||
http := &Http{
|
||||
Servers: []*Server{},
|
||||
Directives: []IDirective{},
|
||||
Comment: block.GetComment(),
|
||||
}
|
||||
|
||||
for _, directive := range block.GetDirectives() {
|
||||
if server, ok := directive.(*Server); ok {
|
||||
http.Servers = append(http.Servers, server)
|
||||
continue
|
||||
}
|
||||
http.Directives = append(http.Directives, directive)
|
||||
}
|
||||
|
||||
return http, nil
|
||||
}
|
||||
return nil, errors.New("http directive must have a block")
|
||||
}
|
||||
|
||||
func (h *Http) GetName() string {
|
||||
return "http"
|
||||
}
|
||||
|
||||
func (h *Http) GetParameters() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (h *Http) GetDirectives() []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
directives = append(directives, h.Directives...)
|
||||
for _, directive := range h.Servers {
|
||||
directives = append(directives, directive)
|
||||
}
|
||||
return directives
|
||||
}
|
||||
|
||||
func (h *Http) FindDirectives(directiveName string) []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, directive := range h.GetDirectives() {
|
||||
if directive.GetName() == directiveName {
|
||||
directives = append(directives, directive)
|
||||
}
|
||||
if directive.GetBlock() != nil {
|
||||
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
|
||||
}
|
||||
}
|
||||
|
||||
return directives
|
||||
}
|
||||
|
||||
func (h *Http) UpdateDirectives(directiveName string, directive Directive) {
|
||||
directives := make([]IDirective, len(h.GetDirectives()))
|
||||
index := -1
|
||||
for i, dir := range h.GetDirectives() {
|
||||
if dir.GetName() == directiveName {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index > -1 {
|
||||
directives[index] = &directive
|
||||
} else {
|
||||
directives = append(directives, &directive)
|
||||
}
|
||||
h.Directives = directives
|
||||
}
|
||||
|
||||
func (h *Http) AddDirectives(directive Directive) {
|
||||
directives := append(h.GetDirectives(), &directive)
|
||||
h.Directives = directives
|
||||
}
|
||||
|
||||
func (h *Http) GetBlock() IBlock {
|
||||
return h
|
||||
}
|
32
backend/utils/nginx/components/location.go
Normal file
32
backend/utils/nginx/components/location.go
Normal file
@ -0,0 +1,32 @@
|
||||
package components
|
||||
|
||||
type Location struct {
|
||||
*Directive
|
||||
Modifier string
|
||||
Match string
|
||||
}
|
||||
|
||||
func NewLocation(directive *Directive) *Location {
|
||||
location := &Location{
|
||||
Modifier: "",
|
||||
Match: "",
|
||||
Directive: directive,
|
||||
}
|
||||
if directive.GetBlock() != nil {
|
||||
directive.Comment = directive.GetBlock().GetComment()
|
||||
}
|
||||
|
||||
if len(directive.Parameters) == 0 {
|
||||
panic("no enough parameter for location")
|
||||
}
|
||||
|
||||
if len(directive.Parameters) == 1 {
|
||||
location.Match = directive.Parameters[0]
|
||||
return location
|
||||
} else if len(directive.Parameters) == 2 {
|
||||
location.Modifier = directive.Parameters[0]
|
||||
location.Match = directive.Parameters[1]
|
||||
return location
|
||||
}
|
||||
return nil
|
||||
}
|
107
backend/utils/nginx/components/server.go
Normal file
107
backend/utils/nginx/components/server.go
Normal file
@ -0,0 +1,107 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Comment string
|
||||
Listens []*ServerListen
|
||||
Directives []IDirective
|
||||
}
|
||||
|
||||
func NewServer(directive IDirective) (*Server, error) {
|
||||
server := &Server{}
|
||||
if block := directive.GetBlock(); block != nil {
|
||||
server.Comment = block.GetComment()
|
||||
directives := block.GetDirectives()
|
||||
for _, dir := range directives {
|
||||
if dir.GetName() == "listen" {
|
||||
server.Listens = append(server.Listens, NewServerListen(dir.GetParameters()))
|
||||
} else {
|
||||
server.Directives = append(server.Directives, dir)
|
||||
}
|
||||
}
|
||||
return server, nil
|
||||
}
|
||||
return nil, errors.New("server directive must have a block")
|
||||
}
|
||||
|
||||
func (s *Server) GetName() string {
|
||||
return "server"
|
||||
}
|
||||
|
||||
func (s *Server) GetParameters() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (s *Server) GetBlock() IBlock {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) GetComment() string {
|
||||
return s.Comment
|
||||
}
|
||||
|
||||
func (s *Server) GetDirectives() []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, ls := range s.Listens {
|
||||
directives = append(directives, ls)
|
||||
}
|
||||
directives = append(directives, s.Directives...)
|
||||
return directives
|
||||
}
|
||||
|
||||
func (s *Server) AddListen(bind string, defaultServer bool, params ...string) {
|
||||
listen := &ServerListen{
|
||||
Bind: bind,
|
||||
Parameters: params,
|
||||
}
|
||||
if defaultServer {
|
||||
listen.DefaultServer = DefaultServer
|
||||
}
|
||||
s.Listens = append(s.Listens, listen)
|
||||
}
|
||||
|
||||
func (s *Server) RemoveListenByBind(bind string) {
|
||||
index := 0
|
||||
listens := s.Listens
|
||||
for _, listen := range s.Listens {
|
||||
if listen.Bind != bind || len(listen.Parameters) > 0 {
|
||||
listens[index] = listen
|
||||
index++
|
||||
}
|
||||
}
|
||||
s.Listens = listens
|
||||
}
|
||||
|
||||
func (s *Server) FindDirectives(directiveName string) []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, directive := range s.Directives {
|
||||
if directive.GetName() == directiveName {
|
||||
directives = append(directives, directive)
|
||||
}
|
||||
if directive.GetBlock() != nil {
|
||||
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
|
||||
}
|
||||
}
|
||||
|
||||
return directives
|
||||
}
|
||||
|
||||
func (s *Server) UpdateDirectives(directiveName string, directive Directive) {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, dir := range s.Directives {
|
||||
if dir.GetName() == directiveName {
|
||||
directives = append(directives, &directive)
|
||||
} else {
|
||||
directives = append(directives, dir)
|
||||
}
|
||||
}
|
||||
s.Directives = directives
|
||||
}
|
||||
|
||||
func (s *Server) AddDirectives(directive Directive) {
|
||||
directives := append(s.Directives, &directive)
|
||||
s.Directives = directives
|
||||
}
|
68
backend/utils/nginx/components/server_listen.go
Normal file
68
backend/utils/nginx/components/server_listen.go
Normal file
@ -0,0 +1,68 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const DefaultServer = "default_server"
|
||||
|
||||
type ServerListen struct {
|
||||
Bind string
|
||||
DefaultServer string
|
||||
Parameters []string
|
||||
Comment string
|
||||
}
|
||||
|
||||
func NewServerListen(params []string) *ServerListen {
|
||||
server := &ServerListen{
|
||||
Parameters: []string{},
|
||||
}
|
||||
for _, param := range params {
|
||||
if isBind(param) {
|
||||
server.Bind = param
|
||||
} else if param == DefaultServer {
|
||||
server.DefaultServer = DefaultServer
|
||||
} else {
|
||||
server.Parameters = append(server.Parameters, param)
|
||||
}
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
func isBind(param string) bool {
|
||||
if common.IsNum(param) {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(param, "*") || strings.Contains(param, ":") || strings.Contains(param, ".") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sl *ServerListen) GetName() string {
|
||||
return "listen"
|
||||
}
|
||||
|
||||
func (sl *ServerListen) GetBlock() IBlock {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sl *ServerListen) GetParameters() []string {
|
||||
params := []string{sl.Bind}
|
||||
params = append(params, sl.DefaultServer)
|
||||
params = append(params, sl.Parameters...)
|
||||
return params
|
||||
}
|
||||
|
||||
func (sl *ServerListen) GetComment() string {
|
||||
return sl.Comment
|
||||
}
|
||||
|
||||
func (sl *ServerListen) AddDefaultServer() {
|
||||
sl.DefaultServer = DefaultServer
|
||||
}
|
||||
|
||||
func (sl *ServerListen) RemoveDefaultServe() {
|
||||
sl.DefaultServer = ""
|
||||
}
|
24
backend/utils/nginx/components/statement.go
Normal file
24
backend/utils/nginx/components/statement.go
Normal file
@ -0,0 +1,24 @@
|
||||
package components
|
||||
|
||||
type IBlock interface {
|
||||
GetDirectives() []IDirective
|
||||
FindDirectives(directiveName string) []IDirective
|
||||
UpdateDirectives(directiveName string, directive Directive)
|
||||
AddDirectives(directive Directive)
|
||||
GetComment() string
|
||||
}
|
||||
|
||||
type IDirective interface {
|
||||
GetName() string
|
||||
GetParameters() []string
|
||||
GetBlock() IBlock
|
||||
GetComment() string
|
||||
}
|
||||
|
||||
type FileDirective interface {
|
||||
isFileDirective()
|
||||
}
|
||||
|
||||
type IncludeDirective interface {
|
||||
FileDirective
|
||||
}
|
94
backend/utils/nginx/components/upstream.go
Normal file
94
backend/utils/nginx/components/upstream.go
Normal file
@ -0,0 +1,94 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Upstream struct {
|
||||
UpstreamName string
|
||||
UpstreamServers []*UpstreamServer
|
||||
Directives []IDirective
|
||||
Comment string
|
||||
}
|
||||
|
||||
func (us *Upstream) GetName() string {
|
||||
return "upstream"
|
||||
}
|
||||
|
||||
func (us *Upstream) GetParameters() []string {
|
||||
return []string{us.UpstreamName}
|
||||
}
|
||||
|
||||
func (us *Upstream) GetBlock() IBlock {
|
||||
return us
|
||||
}
|
||||
|
||||
func (us *Upstream) GetComment() string {
|
||||
return us.Comment
|
||||
}
|
||||
|
||||
func (us *Upstream) GetDirectives() []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
directives = append(directives, us.Directives...)
|
||||
for _, uss := range us.UpstreamServers {
|
||||
directives = append(directives, uss)
|
||||
}
|
||||
|
||||
return directives
|
||||
}
|
||||
|
||||
func NewUpstream(directive IDirective) (*Upstream, error) {
|
||||
parameters := directive.GetParameters()
|
||||
us := &Upstream{
|
||||
UpstreamName: parameters[0],
|
||||
}
|
||||
|
||||
if block := directive.GetBlock(); block != nil {
|
||||
us.Comment = block.GetComment()
|
||||
for _, d := range block.GetDirectives() {
|
||||
if d.GetName() == "server" {
|
||||
us.UpstreamServers = append(us.UpstreamServers, NewUpstreamServer(d))
|
||||
} else {
|
||||
us.Directives = append(us.Directives, d)
|
||||
}
|
||||
}
|
||||
return us, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("missing upstream block")
|
||||
}
|
||||
|
||||
func (us *Upstream) AddServer(server *UpstreamServer) {
|
||||
us.UpstreamServers = append(us.UpstreamServers, server)
|
||||
}
|
||||
|
||||
func (us *Upstream) FindDirectives(directiveName string) []IDirective {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, directive := range us.Directives {
|
||||
if directive.GetName() == directiveName {
|
||||
directives = append(directives, directive)
|
||||
}
|
||||
if directive.GetBlock() != nil {
|
||||
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
|
||||
}
|
||||
}
|
||||
|
||||
return directives
|
||||
}
|
||||
|
||||
func (us *Upstream) UpdateDirectives(directiveName string, directive Directive) {
|
||||
directives := make([]IDirective, 0)
|
||||
for _, dir := range us.GetDirectives() {
|
||||
if dir.GetName() == directiveName {
|
||||
directives = append(directives, &directive)
|
||||
} else {
|
||||
directives = append(directives, dir)
|
||||
}
|
||||
}
|
||||
us.Directives = directives
|
||||
}
|
||||
|
||||
func (us *Upstream) AddDirectives(directive Directive) {
|
||||
directives := append(us.GetDirectives(), &directive)
|
||||
us.Directives = directives
|
||||
}
|
77
backend/utils/nginx/components/upstream_server.go
Normal file
77
backend/utils/nginx/components/upstream_server.go
Normal file
@ -0,0 +1,77 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UpstreamServer struct {
|
||||
Comment string
|
||||
Address string
|
||||
Flags []string
|
||||
Parameters map[string]string
|
||||
}
|
||||
|
||||
func (uss *UpstreamServer) GetName() string {
|
||||
return "server"
|
||||
}
|
||||
|
||||
func (uss *UpstreamServer) GetBlock() IBlock {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uss *UpstreamServer) GetParameters() []string {
|
||||
return uss.GetDirective().Parameters
|
||||
}
|
||||
|
||||
func (uss *UpstreamServer) GetComment() string {
|
||||
return uss.Comment
|
||||
}
|
||||
|
||||
func (uss *UpstreamServer) GetDirective() *Directive {
|
||||
directive := &Directive{
|
||||
Name: "server",
|
||||
Parameters: make([]string, 0),
|
||||
Block: nil,
|
||||
}
|
||||
|
||||
directive.Parameters = append(directive.Parameters, uss.Address)
|
||||
|
||||
paramNames := make([]string, 0)
|
||||
for k := range uss.Parameters {
|
||||
paramNames = append(paramNames, k)
|
||||
}
|
||||
sort.Strings(paramNames)
|
||||
|
||||
for _, k := range paramNames {
|
||||
directive.Parameters = append(directive.Parameters, fmt.Sprintf("%s=%s", k, uss.Parameters[k]))
|
||||
}
|
||||
|
||||
directive.Parameters = append(directive.Parameters, uss.Flags...)
|
||||
|
||||
return directive
|
||||
}
|
||||
|
||||
func NewUpstreamServer(directive IDirective) *UpstreamServer {
|
||||
uss := &UpstreamServer{
|
||||
Comment: directive.GetComment(),
|
||||
Flags: make([]string, 0),
|
||||
Parameters: make(map[string]string, 0),
|
||||
}
|
||||
|
||||
for i, parameter := range directive.GetParameters() {
|
||||
if i == 0 {
|
||||
uss.Address = parameter
|
||||
continue
|
||||
}
|
||||
if strings.Contains(parameter, "=") {
|
||||
s := strings.SplitN(parameter, "=", 2)
|
||||
uss.Parameters[s[0]] = s[1]
|
||||
} else {
|
||||
uss.Flags = append(uss.Flags, parameter)
|
||||
}
|
||||
}
|
||||
|
||||
return uss
|
||||
}
|
132
backend/utils/nginx/dumper.go
Normal file
132
backend/utils/nginx/dumper.go
Normal file
@ -0,0 +1,132 @@
|
||||
package nginx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
//NoIndentStyle default style
|
||||
NoIndentStyle = &Style{
|
||||
SortDirectives: false,
|
||||
StartIndent: 0,
|
||||
Indent: 0,
|
||||
}
|
||||
|
||||
//IndentedStyle default style
|
||||
IndentedStyle = &Style{
|
||||
SortDirectives: false,
|
||||
StartIndent: 0,
|
||||
Indent: 4,
|
||||
}
|
||||
|
||||
//NoIndentSortedStyle default style
|
||||
NoIndentSortedStyle = &Style{
|
||||
SortDirectives: true,
|
||||
StartIndent: 0,
|
||||
Indent: 0,
|
||||
}
|
||||
|
||||
//NoIndentSortedSpaceStyle default style
|
||||
NoIndentSortedSpaceStyle = &Style{
|
||||
SortDirectives: true,
|
||||
SpaceBeforeBlocks: true,
|
||||
StartIndent: 0,
|
||||
Indent: 0,
|
||||
}
|
||||
)
|
||||
|
||||
type Style struct {
|
||||
SortDirectives bool
|
||||
SpaceBeforeBlocks bool
|
||||
StartIndent int
|
||||
Indent int
|
||||
}
|
||||
|
||||
func NewStyle() *Style {
|
||||
style := &Style{
|
||||
SortDirectives: false,
|
||||
StartIndent: 0,
|
||||
Indent: 4,
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
func (s *Style) Iterate() *Style {
|
||||
newStyle := &Style{
|
||||
SortDirectives: s.SortDirectives,
|
||||
SpaceBeforeBlocks: s.SpaceBeforeBlocks,
|
||||
StartIndent: s.StartIndent + s.Indent,
|
||||
Indent: s.Indent,
|
||||
}
|
||||
return newStyle
|
||||
}
|
||||
|
||||
func DumpDirective(d components.IDirective, style *Style) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if style.SpaceBeforeBlocks && d.GetBlock() != nil {
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s%s", strings.Repeat(" ", style.StartIndent), d.GetName()))
|
||||
if len(d.GetParameters()) > 0 {
|
||||
buf.WriteString(fmt.Sprintf(" %s", strings.Join(d.GetParameters(), " ")))
|
||||
}
|
||||
if d.GetBlock() == nil {
|
||||
if d.GetName() != "" {
|
||||
buf.WriteRune(';')
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
if d.GetComment() != "" {
|
||||
buf.WriteString(d.GetComment())
|
||||
}
|
||||
} else {
|
||||
buf.WriteString(" {")
|
||||
if d.GetComment() != "" {
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(d.GetComment())
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(DumpBlock(d.GetBlock(), style.Iterate()))
|
||||
buf.WriteString(fmt.Sprintf("\n%s}", strings.Repeat(" ", style.StartIndent)))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func DumpBlock(b components.IBlock, style *Style) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
directives := b.GetDirectives()
|
||||
if style.SortDirectives {
|
||||
sort.SliceStable(directives, func(i, j int) bool {
|
||||
return directives[i].GetName() < directives[j].GetName()
|
||||
})
|
||||
}
|
||||
|
||||
for i, directive := range directives {
|
||||
buf.WriteString(DumpDirective(directive, style))
|
||||
if i != len(directives)-1 {
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func DumpConfig(c *components.Config, style *Style) string {
|
||||
return DumpBlock(c.Block, style)
|
||||
}
|
||||
|
||||
func WriteConfig(c *components.Config, style *Style) error {
|
||||
dir, _ := filepath.Split(c.FilePath)
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(c.FilePath, []byte(DumpConfig(c, style)), 0644)
|
||||
}
|
14
backend/utils/nginx/nginx.go
Normal file
14
backend/utils/nginx/nginx.go
Normal file
@ -0,0 +1,14 @@
|
||||
package nginx
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
|
||||
)
|
||||
|
||||
func GetConfig(path string) (*components.Config, error) {
|
||||
p, err := parser.NewParser(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Parse(), nil
|
||||
}
|
82
backend/utils/nginx/parser/flag/flag.go
Normal file
82
backend/utils/nginx/parser/flag/flag.go
Normal file
@ -0,0 +1,82 @@
|
||||
package flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Type int
|
||||
|
||||
const (
|
||||
EOF Type = iota
|
||||
Eol
|
||||
Keyword
|
||||
QuotedString
|
||||
Variable
|
||||
BlockStart
|
||||
BlockEnd
|
||||
Semicolon
|
||||
Comment
|
||||
Illegal
|
||||
Regex
|
||||
)
|
||||
|
||||
var (
|
||||
FlagName = map[Type]string{
|
||||
QuotedString: "QuotedString",
|
||||
EOF: "Eof",
|
||||
Keyword: "Keyword",
|
||||
Variable: "Variable",
|
||||
BlockStart: "BlockStart",
|
||||
BlockEnd: "BlockEnd",
|
||||
Semicolon: "Semicolon",
|
||||
Comment: "Comment",
|
||||
Illegal: "Illegal",
|
||||
Regex: "Regex",
|
||||
}
|
||||
)
|
||||
|
||||
func (tt Type) String() string {
|
||||
return FlagName[tt]
|
||||
}
|
||||
|
||||
type Flag struct {
|
||||
Type Type
|
||||
Literal string
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
|
||||
func (t Flag) String() string {
|
||||
return fmt.Sprintf("{Type:%s,Literal:\"%s\",Line:%d,Column:%d}", t.Type, t.Literal, t.Line, t.Column)
|
||||
}
|
||||
|
||||
func (t Flag) Lit(literal string) Flag {
|
||||
t.Literal = literal
|
||||
return t
|
||||
}
|
||||
|
||||
func (t Flag) EqualTo(t2 Flag) bool {
|
||||
return t.Type == t2.Type && t.Literal == t2.Literal
|
||||
}
|
||||
|
||||
type Flags []Flag
|
||||
|
||||
func (fs Flags) EqualTo(flags Flags) bool {
|
||||
if len(fs) != len(flags) {
|
||||
return false
|
||||
}
|
||||
for i, t := range fs {
|
||||
if !t.EqualTo(flags[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (t Flag) Is(typ Type) bool {
|
||||
return t.Type == typ
|
||||
}
|
||||
|
||||
func (t Flag) IsParameterEligible() bool {
|
||||
return t.Is(Keyword) || t.Is(QuotedString) || t.Is(Variable) || t.Is(Regex)
|
||||
}
|
209
backend/utils/nginx/parser/lexer.go
Normal file
209
backend/utils/nginx/parser/lexer.go
Normal file
@ -0,0 +1,209 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
|
||||
"io"
|
||||
)
|
||||
|
||||
type lexer struct {
|
||||
reader *bufio.Reader
|
||||
file string
|
||||
line int
|
||||
column int
|
||||
Latest flag.Flag
|
||||
}
|
||||
|
||||
func lex(content string) *lexer {
|
||||
return newLexer(bytes.NewBuffer([]byte(content)))
|
||||
}
|
||||
|
||||
func newLexer(r io.Reader) *lexer {
|
||||
return &lexer{
|
||||
line: 1,
|
||||
reader: bufio.NewReader(r),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *lexer) scan() flag.Flag {
|
||||
s.Latest = s.getNextFlag()
|
||||
return s.Latest
|
||||
}
|
||||
|
||||
//func (s *lexer) all() flag.Flags {
|
||||
// tokens := make([]flag.Flag, 0)
|
||||
// for {
|
||||
// v := s.scan()
|
||||
// if v.Type == flag.EOF || v.Type == -1 {
|
||||
// break
|
||||
// }
|
||||
// tokens = append(tokens, v)
|
||||
// }
|
||||
// return tokens
|
||||
//}
|
||||
|
||||
func (s *lexer) getNextFlag() flag.Flag {
|
||||
retoFlag:
|
||||
ch := s.peek()
|
||||
switch {
|
||||
case isSpace(ch):
|
||||
s.skipWhitespace()
|
||||
goto retoFlag
|
||||
case isEOF(ch):
|
||||
return s.NewToken(flag.EOF).Lit(string(s.read()))
|
||||
case ch == ';':
|
||||
return s.NewToken(flag.Semicolon).Lit(string(s.read()))
|
||||
case ch == '{':
|
||||
return s.NewToken(flag.BlockStart).Lit(string(s.read()))
|
||||
case ch == '}':
|
||||
return s.NewToken(flag.BlockEnd).Lit(string(s.read()))
|
||||
case ch == '#':
|
||||
return s.scanComment()
|
||||
case ch == '$':
|
||||
return s.scanVariable()
|
||||
case isQuote(ch):
|
||||
return s.scanQuotedString(ch)
|
||||
default:
|
||||
return s.scanKeyword()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *lexer) peek() rune {
|
||||
r, _, _ := s.reader.ReadRune()
|
||||
_ = s.reader.UnreadRune()
|
||||
return r
|
||||
}
|
||||
|
||||
type runeCheck func(rune) bool
|
||||
|
||||
func (s *lexer) readUntil(until runeCheck) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
for {
|
||||
if ch := s.peek(); isEOF(ch) {
|
||||
break
|
||||
} else if until(ch) {
|
||||
break
|
||||
} else {
|
||||
buf.WriteRune(s.read())
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (s *lexer) NewToken(tokenType flag.Type) flag.Flag {
|
||||
return flag.Flag{
|
||||
Type: tokenType,
|
||||
Line: s.line,
|
||||
Column: s.column,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *lexer) readWhile(while runeCheck) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
for {
|
||||
if ch := s.peek(); while(ch) {
|
||||
buf.WriteRune(s.read())
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (s *lexer) skipWhitespace() {
|
||||
s.readWhile(isSpace)
|
||||
}
|
||||
|
||||
func (s *lexer) scanComment() flag.Flag {
|
||||
return s.NewToken(flag.Comment).Lit(s.readUntil(isEndOfLine))
|
||||
}
|
||||
|
||||
func (s *lexer) scanQuotedString(delimiter rune) flag.Flag {
|
||||
var buf bytes.Buffer
|
||||
tok := s.NewToken(flag.QuotedString)
|
||||
buf.WriteRune(s.read()) //consume delimiter
|
||||
for {
|
||||
ch := s.read()
|
||||
|
||||
if ch == rune(flag.EOF) {
|
||||
panic("unexpected end of file while scanning a string, maybe an unclosed quote?")
|
||||
}
|
||||
|
||||
if ch == '\\' {
|
||||
if needsEscape(s.peek(), delimiter) {
|
||||
switch s.read() {
|
||||
case 'n':
|
||||
buf.WriteRune('\n')
|
||||
case 'r':
|
||||
buf.WriteRune('\r')
|
||||
case 't':
|
||||
buf.WriteRune('\t')
|
||||
case '\\':
|
||||
buf.WriteRune('\\')
|
||||
case delimiter:
|
||||
buf.WriteRune(delimiter)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
if ch == delimiter {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return tok.Lit(buf.String())
|
||||
}
|
||||
|
||||
func (s *lexer) scanKeyword() flag.Flag {
|
||||
return s.NewToken(flag.Keyword).Lit(s.readUntil(isKeywordTerminator))
|
||||
}
|
||||
|
||||
func (s *lexer) scanVariable() flag.Flag {
|
||||
return s.NewToken(flag.Variable).Lit(s.readUntil(isKeywordTerminator))
|
||||
}
|
||||
|
||||
func (s *lexer) read() rune {
|
||||
ch, _, err := s.reader.ReadRune()
|
||||
if err != nil {
|
||||
return rune(flag.EOF)
|
||||
}
|
||||
|
||||
if ch == '\n' {
|
||||
s.column = 1
|
||||
s.line++
|
||||
} else {
|
||||
s.column++
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
func isQuote(ch rune) bool {
|
||||
return ch == '"' || ch == '\'' || ch == '`'
|
||||
}
|
||||
|
||||
func isKeywordTerminator(ch rune) bool {
|
||||
return isSpace(ch) || isEndOfLine(ch) || ch == '{' || ch == ';'
|
||||
}
|
||||
|
||||
func needsEscape(ch, delimiter rune) bool {
|
||||
return ch == delimiter || ch == 'n' || ch == 't' || ch == '\\' || ch == 'r'
|
||||
}
|
||||
|
||||
func isSpace(ch rune) bool {
|
||||
return ch == ' ' || ch == '\t' || isEndOfLine(ch)
|
||||
}
|
||||
|
||||
func isEOF(ch rune) bool {
|
||||
return ch == rune(flag.EOF)
|
||||
}
|
||||
|
||||
func isEndOfLine(ch rune) bool {
|
||||
return ch == '\r' || ch == '\n'
|
||||
}
|
172
backend/utils/nginx/parser/parser.go
Normal file
172
backend/utils/nginx/parser/parser.go
Normal file
@ -0,0 +1,172 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
lexer *lexer
|
||||
currentToken flag.Flag
|
||||
followingToken flag.Flag
|
||||
blockWrappers map[string]func(*components.Directive) components.IDirective
|
||||
directiveWrappers map[string]func(*components.Directive) components.IDirective
|
||||
}
|
||||
|
||||
func NewStringParser(str string) *Parser {
|
||||
return NewParserFromLexer(lex(str))
|
||||
}
|
||||
|
||||
func NewParser(filePath string) (*Parser, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := newLexer(bufio.NewReader(f))
|
||||
l.file = filePath
|
||||
p := NewParserFromLexer(l)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func NewParserFromLexer(lexer *lexer) *Parser {
|
||||
parser := &Parser{
|
||||
lexer: lexer,
|
||||
}
|
||||
|
||||
parser.nextToken()
|
||||
parser.nextToken()
|
||||
|
||||
parser.blockWrappers = map[string]func(*components.Directive) components.IDirective{
|
||||
"http": func(directive *components.Directive) components.IDirective {
|
||||
return parser.wrapHttp(directive)
|
||||
},
|
||||
"server": func(directive *components.Directive) components.IDirective {
|
||||
return parser.wrapServer(directive)
|
||||
},
|
||||
"location": func(directive *components.Directive) components.IDirective {
|
||||
return parser.wrapLocation(directive)
|
||||
},
|
||||
"upstream": func(directive *components.Directive) components.IDirective {
|
||||
return parser.wrapUpstream(directive)
|
||||
},
|
||||
}
|
||||
|
||||
parser.directiveWrappers = map[string]func(*components.Directive) components.IDirective{
|
||||
"server": func(directive *components.Directive) components.IDirective {
|
||||
return parser.parseUpstreamServer(directive)
|
||||
},
|
||||
}
|
||||
|
||||
return parser
|
||||
}
|
||||
|
||||
func (p *Parser) nextToken() {
|
||||
p.currentToken = p.followingToken
|
||||
p.followingToken = p.lexer.scan()
|
||||
}
|
||||
|
||||
func (p *Parser) curTokenIs(t flag.Type) bool {
|
||||
return p.currentToken.Type == t
|
||||
}
|
||||
|
||||
func (p *Parser) followingTokenIs(t flag.Type) bool {
|
||||
return p.followingToken.Type == t
|
||||
}
|
||||
|
||||
func (p *Parser) Parse() *components.Config {
|
||||
return &components.Config{
|
||||
FilePath: p.lexer.file,
|
||||
Block: p.parseBlock(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseBlock() *components.Block {
|
||||
|
||||
context := &components.Block{
|
||||
Comment: "",
|
||||
Directives: make([]components.IDirective, 0),
|
||||
}
|
||||
|
||||
parsingloop:
|
||||
for {
|
||||
switch {
|
||||
case p.curTokenIs(flag.EOF) || p.curTokenIs(flag.BlockEnd):
|
||||
break parsingloop
|
||||
case p.curTokenIs(flag.Keyword):
|
||||
context.Directives = append(context.Directives, p.parseStatement())
|
||||
case p.curTokenIs(flag.Comment):
|
||||
context.Directives = append(context.Directives, &components.Comment{
|
||||
Detail: p.currentToken.Literal,
|
||||
})
|
||||
}
|
||||
p.nextToken()
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func (p *Parser) parseStatement() components.IDirective {
|
||||
d := &components.Directive{
|
||||
Name: p.currentToken.Literal,
|
||||
}
|
||||
|
||||
for p.nextToken(); p.currentToken.IsParameterEligible(); p.nextToken() {
|
||||
d.Parameters = append(d.Parameters, p.currentToken.Literal)
|
||||
}
|
||||
|
||||
if p.curTokenIs(flag.Semicolon) {
|
||||
if dw, ok := p.directiveWrappers[d.Name]; ok {
|
||||
return dw(d)
|
||||
}
|
||||
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
|
||||
d.Comment = p.followingToken.Literal
|
||||
p.nextToken()
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
if p.curTokenIs(flag.BlockStart) {
|
||||
|
||||
inLineComment := ""
|
||||
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
|
||||
inLineComment = p.followingToken.Literal
|
||||
p.nextToken()
|
||||
p.nextToken()
|
||||
}
|
||||
block := p.parseBlock()
|
||||
block.Comment = inLineComment
|
||||
d.Block = block
|
||||
if bw, ok := p.blockWrappers[d.Name]; ok {
|
||||
return bw(d)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
panic(fmt.Errorf("unexpected token %s (%s) on line %d, column %d", p.currentToken.Type.String(), p.currentToken.Literal, p.currentToken.Line, p.currentToken.Column))
|
||||
}
|
||||
|
||||
func (p *Parser) wrapLocation(directive *components.Directive) *components.Location {
|
||||
return components.NewLocation(directive)
|
||||
}
|
||||
|
||||
func (p *Parser) wrapServer(directive *components.Directive) *components.Server {
|
||||
s, _ := components.NewServer(directive)
|
||||
return s
|
||||
}
|
||||
|
||||
func (p *Parser) wrapUpstream(directive *components.Directive) *components.Upstream {
|
||||
s, _ := components.NewUpstream(directive)
|
||||
return s
|
||||
}
|
||||
|
||||
func (p *Parser) wrapHttp(directive *components.Directive) *components.Http {
|
||||
h, _ := components.NewHttp(directive)
|
||||
return h
|
||||
}
|
||||
|
||||
func (p *Parser) parseUpstreamServer(directive *components.Directive) *components.UpstreamServer {
|
||||
return components.NewUpstreamServer(directive)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user