test stage 1

This commit is contained in:
Sense T 2024-04-19 09:44:37 +08:00
parent b583720223
commit 88b2255f8b
20 changed files with 131 additions and 95 deletions

13
.nixd.json Normal file
View File

@ -0,0 +1,13 @@
{
"formatting": {
"command": "nixpkgs-fmt"
},
"eval": {
"target": {
"args": [
"--expr",
"with import <nixpkgs> { };"
]
}
}
}

17
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
"sqltools.connections": [
{
"mysqlOptions": {
"authProtocol": "default",
"enableSsl": "Disabled"
},
"previewLimit": 50,
"server": "mysql.dev",
"port": 3306,
"driver": "MySQL",
"name": "recored-ui",
"database": "recoredui",
"username": "recoredui"
}
]
}

1
TODO
View File

@ -2,6 +2,7 @@
- [x] Web UI
- [x] i18n
- [x] modals
- [] debug
- [] swagger
- [] Nix Module
- [] RBAC

View File

@ -18,12 +18,12 @@ var Command = &cli.Command{
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "listen",
Value: "[::]",
Value: "::",
Usage: "IP for listen at",
}),
altsrc.NewIntFlag(&cli.IntFlag{
Name: "port",
Value: 8080,
Value: 3000,
Usage: "Port for listen at",
}),
},

View File

@ -5,6 +5,8 @@ import (
"reCoreD-UI/database"
"reCoreD-UI/models"
"strconv"
"github.com/sirupsen/logrus"
)
type domainsDAO struct {
@ -28,6 +30,7 @@ func CreateDomain(d *models.Domain) (*models.Domain, error) {
r.Name = "@"
r.RecordType = models.RecordTypeSOA
r.Content = d.GenerateSOA()
logrus.Debug(r)
if err := r.CheckZone(); err != nil {
tx.Rollback()
return nil, err
@ -41,7 +44,7 @@ func CreateDomain(d *models.Domain) (*models.Domain, error) {
for i, ns := range nss {
record := &models.Record[models.NSRecord]{
Zone: d.WithDotEnd(),
RecordType: models.RecordTypeSOA,
RecordType: models.RecordTypeNS,
Name: fmt.Sprintf("ns%d", i+1),
}
record.Content.Host = ns
@ -123,13 +126,13 @@ func DeleteDomain(id string) error {
}
tx := database.Client.Begin()
domain, err := (domainsDAO{}).GetOne(tx, &models.Domain{ID: ID})
domain, err := (domainsDAO{}).GetOne(tx, &models.Domain{ID: uint(ID)})
if err != nil {
tx.Rollback()
return err
}
if err := (domainsDAO{}).Delete(tx, &models.Domain{ID: ID}); err != nil {
if err := (domainsDAO{}).Delete(tx, &models.Domain{ID: uint(ID)}); err != nil {
tx.Rollback()
return err
}

View File

@ -37,7 +37,7 @@ func RegisterMetrics() {
GinMetrics := ginprometheus.NewPrometheus("recoredui")
for _, v := range GinMetrics.MetricsList {
prometheus.MustRegister(v.MetricCollector)
prometheus.Register(v.MetricCollector)
}
}

View File

@ -68,7 +68,7 @@ func DeleteRecord(domain, id string) error {
}
tx := database.Client.Begin()
record, err := (recordsDAO{}).GetOne(tx, &models.Record[models.RecordContentDefault]{ID: ID, Zone: fmt.Sprintf("%s.", domain)})
record, err := (recordsDAO{}).GetOne(tx, &models.Record[models.RecordContentDefault]{ID: uint(ID), Zone: fmt.Sprintf("%s.", domain)})
if err != nil {
tx.Rollback()
return err

View File

@ -3,6 +3,7 @@ package database
import (
"errors"
"github.com/huandu/go-clone"
"gorm.io/gorm"
)
@ -14,41 +15,40 @@ func (b BaseDAO[T]) Migrate(db *gorm.DB, e T) error {
func (BaseDAO[T]) GetAll(db *gorm.DB, e T, cond ...T) ([]T, error) {
var r []T
tx := db
tx := db.Model(e)
for _, c := range cond {
tx = tx.Where(c)
}
if err := tx.Find(&r, e).Error; err != nil {
rows, err := tx.Rows()
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
if err := db.ScanRows(rows, e); err != nil {
return nil, err
}
i := clone.Clone(e).(T)
r = append(r, i)
}
return r, nil
}
func (BaseDAO[T]) GetOne(db *gorm.DB, e T, cond ...T) (T, error) {
var r T
tx := db
for _, c := range cond {
tx = tx.Where(c)
}
if err := tx.First(&r, e).Error; err != nil {
return r, err
if err := tx.First(e).Error; err != nil {
return e, err
}
return r, nil
}
func (BaseDAO[T]) GetSome(db *gorm.DB, e T, limit, offset int, cond ...T) ([]T, error) {
var r []T
tx := db
for _, c := range cond {
tx = tx.Where(c)
}
if err := tx.Find(&r, e).Limit(limit).Offset(offset).Error; err != nil {
return nil, err
}
return r, nil
return e, nil
}
func (BaseDAO[T]) Create(db *gorm.DB, e T) (T, error) {

View File

@ -1,39 +1,6 @@
{
"nodes": {
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1698420672,
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
"owner": "nix-community",
"repo": "naersk",
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "master",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1711715736,
"narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "807c549feabce7eddbf259dbdcec9e0600a0660d",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1711715736,
"narHash": "sha256-9slQ609YqT9bT/MNX9+5k5jltL9zgpn36DpFB7TkttM=",
@ -51,8 +18,7 @@
},
"root": {
"inputs": {
"naersk": "naersk",
"nixpkgs": "nixpkgs_2",
"nixpkgs": "nixpkgs",
"utils": "utils"
}
},

View File

@ -20,29 +20,23 @@
};
inputs = {
naersk.url = "github:nix-community/naersk/master";
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, utils, naersk }:
outputs = { self, nixpkgs, utils }:
utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
naersk-lib = pkgs.callPackage naersk { };
in
{
defaultPackage = naersk-lib.buildPackage {
src = ./.;
buildInputs = with pkgs; [
];
};
defaultPackage = {};
devShell = with pkgs; mkShell {
buildInputs = [
go
nodejs
dig
tokei
];
GOPATH = "/home/coder/.cache/go";

1
go.mod
View File

@ -35,6 +35,7 @@ require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/huandu/go-clone v1.7.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect

3
go.sum
View File

@ -354,6 +354,9 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U=
github.com/huandu/go-clone v1.7.2 h1:3+Aq0Ed8XK+zKkLjE2dfHg0XrpIfcohBE1K+c8Usxoo=
github.com/huandu/go-clone v1.7.2/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5TbTVaXE=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=

View File

@ -6,17 +6,17 @@ import (
)
type Domain struct {
ID int `gorm:"primaryKey" json:"id"`
ID uint `gorm:"primaryKey" json:"id"`
DomainName string `gorm:"unique,not null,size:255" json:"domain_name"`
//SOA Info
MainDNS string `gorm:"not null;size:255" json:"main_dns"`
AdminEmail string `gorm:"not null;size:255" json:"admin_email"`
SerialNumber int64 `gorm:"not null;default:1" json:"serial_number"`
RefreshInterval uint32 `gorm:"not null;size:255,default:\"86400\"" json:"refresh_interval"`
RetryInterval uint32 `gorm:"not null;size:255,default:\"7200\"" json:"retry_interval"`
ExpiryPeriod uint32 `gorm:"not null;size:255,default:\"3600000\"" json:"expiry_period"`
NegativeTtl uint32 `gorm:"not null;size:255,default:\"86400\"" json:"negative_ttl"`
RefreshInterval uint32 `gorm:"type:uint;not null;default:86400" json:"refresh_interval"`
RetryInterval uint32 `gorm:"type:uint;not null;default:7200" json:"retry_interval"`
ExpiryPeriod uint32 `gorm:"type:uint;not null;default:3600000" json:"expiry_period"`
NegativeTtl uint32 `gorm:"type:uint;not null;default:86400" json:"negative_ttl"`
}
func (d *Domain) EmailSOAForamt() string {
@ -53,8 +53,13 @@ func (d *Domain) GenerateSOA() SOARecord {
return r
}
func (d *Domain) GetValue() Domain {
return *d
}
type IDomain interface {
EmailSOAForamt() string
WithDotEnd() string
GenerateSOA() SOARecord
GetValue() Domain
}

View File

@ -25,7 +25,7 @@ type recordContentTypes interface {
}
type Record[T recordContentTypes] struct {
ID int `gorm:"primaryKey" json:"id"`
ID uint `gorm:"primaryKey" json:"id"`
Zone string `gorm:"not null;size:255" json:"zone"`
Name string `gorm:"not null;size:255" json:"name"`
Ttl int `json:"ttl"`
@ -34,11 +34,11 @@ type Record[T recordContentTypes] struct {
}
func (*Record[T]) TableName() string {
return "coredns_record"
return "coredns_records"
}
func (r *Record[T]) CheckZone() error {
if strings.HasSuffix(r.Zone, ".") {
if !strings.HasSuffix(r.Zone, ".") {
return ErrorZoneNotEndWithDot
}
return nil
@ -65,6 +65,10 @@ func (r *Record[T]) GetType() string {
return r.RecordType
}
func (r *Record[T]) GetValue() IRecord {
return r.ToEntity()
}
type IRecord interface {
TableName() string
CheckZone() error
@ -72,4 +76,5 @@ type IRecord interface {
ToEntity() IRecord
FromEntity(any) error
GetType() string
GetValue() IRecord
}

View File

@ -2,8 +2,6 @@ package models
import (
"fmt"
"gorm.io/gorm"
)
const (
@ -13,7 +11,7 @@ const (
)
type Settings struct {
gorm.Model
ID uint `gorm:"primaryKey"`
Key string `gorm:"unique;not null;size:255"`
Value string `gorm:"not null;size:255"`
}
@ -22,6 +20,11 @@ func (s *Settings) String() string {
return fmt.Sprintf("%s: %s", s.Key, s.Value)
}
func (s *Settings) GetValue() Settings {
return *s
}
type ISettings interface {
String() string
GetValue() Settings
}

View File

@ -4,7 +4,6 @@ import (
"net/http"
"path"
"reCoreD-UI/controllers"
"strings"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus/promhttp"
@ -24,11 +23,21 @@ func (s *Server) setupRoute() {
if err != nil {
logrus.Fatal(err)
}
logrus.Debugf("got %s:%s", username, password)
swaggerHandler := gin.New()
swaggerHandler.GET(path.Join(swaggerPrefix, "*any"), ginSwagger.WrapHandler(swaggerfiles.Handler))
server := s.webServer.Group(s.prefix)
metricHandler := gin.New()
swaggerHandler := server
if s.debug {
swaggerHandler.GET(path.Join(swaggerPrefix, "*any"), ginSwagger.WrapHandler(swaggerfiles.Handler))
} else {
swaggerHandler.GET(path.Join(swaggerPrefix, "*any"), func(ctx *gin.Context) {
ctx.HTML(http.StatusNotFound, "", nil)
})
}
controllers.RegisterMetrics()
metricHandler := server
metricHandler.GET(metricPrefix, func(ctx *gin.Context) {
if err := controllers.RefreshMetrics(); err != nil {
logrus.Error(err)
@ -36,11 +45,18 @@ func (s *Server) setupRoute() {
promhttp.Handler().ServeHTTP(ctx.Writer, ctx.Request)
})
apiHandler := gin.New()
apiHandler := server
groupV1 := apiHandler.Group(apiPrefix, gin.BasicAuth(gin.Accounts{
username: password,
})).Group("/v1")
}), func(ctx *gin.Context) {
_, ok := ctx.Get(gin.AuthUserKey)
if !ok {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, Response{
Succeed: false,
})
}
}).Group("/v1")
domains := groupV1.Group("/domains")
domains.
@ -57,15 +73,15 @@ func (s *Server) setupRoute() {
PUT("/:domain", updateRecord).
DELETE("/:domain/:id", deleteRecord)
server := s.webServer.Group(s.prefix)
server.Use(func(ctx *gin.Context) {
/*server := s.webServer.Group(s.prefix)
server.Use(apiHandler.HandleContext, metricHandler.HandleContext, func(ctx *gin.Context) {
uri := ctx.Request.RequestURI
logrus.Debug(uri)
switch {
case strings.HasPrefix(uri, path.Join(s.prefix, apiPrefix)):
apiHandler.HandleContext(ctx)
//apiHandler.HandleContext(ctx)
case strings.HasPrefix(uri, path.Join(s.prefix, metricPrefix)):
metricHandler.HandleContext(ctx)
//metricHandler.HandleContext(ctx)
case strings.HasPrefix(uri, path.Join(s.prefix, swaggerPrefix)):
if s.debug {
swaggerHandler.HandleContext(ctx)
@ -75,5 +91,8 @@ func (s *Server) setupRoute() {
default:
staticFileHandler()(ctx)
}
})
})*/
server.GET("/", staticFileHandler())
server.GET("/assets/*any", staticFileHandler())
}

View File

@ -22,6 +22,9 @@ func NewServer(c *cli.Context) (*Server, error) {
}
if c.Bool("debug") {
database.Client = database.Client.Debug()
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
return &Server{

View File

@ -91,7 +91,8 @@ export default {
dotAndMinus: 'should not start or end with "." "-"',
doubleDots: 'should have no contianus "."',
logerThan63: 'should not longer than 63 characters splited by "."'
}
},
tooLong: 'too long'
}
}
}

View File

@ -91,7 +91,8 @@ export default {
dotAndMinus: '资源记录不能以 "."、"-" 开头或结尾',
doubleDots: '资源记录不能有连续的 "."',
logerThan63: '资源记录以 "." 分割的每个字符串长度不能超过63字符'
}
},
tooLong: '记录值过长'
}
}
}

View File

@ -35,6 +35,7 @@ export class TXTRecord {
static validate(v: TXTRecord): true | Error {
if (!v.text || v.text === '') return new Error(t('common.mandatory'))
if (v.text.length > 512) return new Error('records.errors.tooLong')
return true
}