Compare commits

..

No commits in common. "main" and "next" have entirely different histories.
main ... next

161 changed files with 3604 additions and 6757 deletions

View File

View File

@ -12,7 +12,6 @@ Nuxt-Whois 是一个基于 Nuxt3、Tailwind CSS 和 Xep-Whois 构建的Whois查
## 更新说明
- 2024.4.7 增加 Go 后端 完善后台管理 增加用户自定义主题色 (目前基础合并,三天后会提交正式,禁止同步当前)
- 2024.3.25 抛弃原来 NuxtUi 改用 NaiveUi 重构中 后台增加中 当前版本无法上线使用
- 2024.3.18 重构V2版本 预计三天内完成。

27
app.config.ts Normal file
View File

@ -0,0 +1,27 @@
import {_colors, _fontFamily} from "#tailwind-config/theme.mjs";
export default defineAppConfig({
naiveui: {
themeConfig: {
shared: {
common: {
fontFamily: _fontFamily.sans.join(", "),
},
},
light: {
common: {
primaryColor: _colors.blue[600],
primaryColorHover: _colors.blue[500],
primaryColorPressed: _colors.blue[700],
},
},
dark: {
common: {
primaryColor: _colors.blue[500],
primaryColorHover: _colors.blue[400],
primaryColorPressed: _colors.blue[600],
},
},
},
},
});

View File

@ -11,6 +11,7 @@
<NuxtLayout>
<NuxtLoadingIndicator/>
<NuxtPage/>
<!-- <CommonLayoutSetting v-if="isAdminRoute" class="fixed right-12 top-1/2 z-999" />-->
</NuxtLayout>
</n-message-provider>
</n-modal-provider>
@ -18,26 +19,21 @@
</template>
<script setup lang="ts">
import {darkTheme, lightTheme} from 'naive-ui'
import {naiveThemeOverrides} from "~/settings/settings";
const colorMode = useColorMode()
const themeOverrides = naiveThemeOverrides
const theme = computed(() => {
return colorMode.value === 'system' ? (colorMode.value ? lightTheme : darkTheme) : colorMode.value === 'light' ? lightTheme : darkTheme
})
const styleStore = useStyleStore()
const {common} = storeToRefs(styleStore)
const themeOverrides = computed(() => {
return {
common: common.value, // 使 common.value
}
})
await callOnce(async () => {
await useSettingsStore().webSiteConfigInit()
await useConfigStore().configServerInit()
})
const whoisStore = useWhoisStore()
const dnsStore = useDnsStore()
const domainStore = useDomainStore()
whoisStore.newWhoisList()
dnsStore.newDnsList()
domainStore.newDomainList()
</script>
<style>

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

3
backend/.gitignore vendored
View File

@ -1,3 +0,0 @@
/storage/
.idea
*.log

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Nunu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,47 +0,0 @@
# Whois Go
等待完善
## Features
- **Gin**: https://github.com/gin-gonic/gin
- **Wire**: https://github.com/google/wire
- **Viper**: https://github.com/spf13/viper
- More...
## 项目结构
```
.
├── cmd
│ └── server
│ ├── main.go
│ ├── wire.go
│ └── wire_gen.go
├── config
│ ├── local.yml
│ └── prod.yml
├── internal
│ ├── handler
│ │ ├── handler.go
│ │ └── user.go
│ ├── middleware
│ │ └── cors.go
│ ├── model
│ │ └── user.go
│ ├── repository
│ │ ├── repository.go
│ │ └── user.go
│ ├── server
│ │ └── http.go
│ └── service
│ ├── service.go
│ └── user.go
├── pkg
├── LICENSE
├── README.md
├── README_zh.md
├── go.mod
└── go.sum
```

View File

@ -1,25 +0,0 @@
package v1
// WhoisApiInterface is the interface for WhoisApi
type WhoisApiInterface struct {
Label string `json:"label"`
Name string `json:"name"`
Order int `json:"order"`
Show bool `json:"show"`
Disabled bool `json:"disabled"`
}
// DNSApiInterface is the interface for DNSApi
type DNSApiInterface struct {
Label string `json:"label"`
Name string `json:"name"`
Order int `json:"order"`
Show bool `json:"show"`
Disabled bool `json:"disabled"`
IName string `json:"iName"`
Flag string `json:"flag"`
}
type FileConfig struct {
Config string `json:"config" binding:"required"`
}

View File

@ -1,62 +0,0 @@
package v1
import (
"github.com/gin-gonic/gin"
"net/http"
)
type Response struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Msg string `json:"msg"`
}
const (
ERROR = 500
SUCCESS = 200
)
func Result(code int, data interface{}, msg string, c *gin.Context) {
// 开始时间
c.JSON(http.StatusOK, Response{
code,
data,
msg,
})
}
func Ok(c *gin.Context) {
Result(SUCCESS, map[string]interface{}{}, "操作成功", c)
}
func OkWithMessage(message string, c *gin.Context) {
Result(SUCCESS, map[string]interface{}{}, message, c)
}
func OkWithData(data interface{}, c *gin.Context) {
Result(SUCCESS, data, "查询成功", c)
}
func OkWithDetailed(data interface{}, message string, c *gin.Context) {
Result(SUCCESS, data, message, c)
}
func Fail(c *gin.Context) {
Result(ERROR, map[string]interface{}{}, "操作失败", c)
}
func FailWithMessage(message string, c *gin.Context) {
Result(ERROR, map[string]interface{}{}, message, c)
}
func NoAuth(message string, c *gin.Context) {
c.JSON(http.StatusUnauthorized, Response{
7,
nil,
message,
})
}
func FailWithDetailed(data interface{}, message string, c *gin.Context) {
Result(ERROR, data, message, c)
}

View File

@ -1,82 +0,0 @@
package v1
import "time"
// TianHuWhoisResponse represents the top-level structure of the WHOIS response.
type TianHuWhoisResponse struct {
Code string `json:"code"`
Message string `json:"message"`
Data TianHuWhoisData `json:"data"`
}
// TianHuWhoisData represents the detailed data part of the WHOIS response.
type TianHuWhoisData struct {
URL string `json:"url"`
Result string `json:"result"`
Status int `json:"status"`
Formatted TianHuFormattedWhois `json:"formatted"`
TLD string `json:"tld"`
Timezone TianHuWhoisTimezone `json:"timezone"`
}
// TianHuFormattedWhois represents the formatted WHOIS data.
type TianHuFormattedWhois struct {
Key string `json:"key"`
Domain TianHuDomainInfo `json:"domain"`
Registrar TianHuRegistrarInfo `json:"registrar"`
Registrant TianHuRegistrantInfo `json:"registrant"`
Administrative TianHuAdministrativeInfo `json:"administrative"`
Technical TianHuTechnicalInfo `json:"technical"`
Billing TianHuBillingInfo `json:"billing"`
}
// TianHuDomainInfo contains information about the domain.
type TianHuDomainInfo struct {
NameServers []string `json:"name_servers"`
Status []string `json:"status"`
Domain string `json:"domain"`
ID string `json:"id"`
WhoisServer string `json:"whois_server"`
UpdatedDate time.Time `json:"updated_date"`
CreatedDate time.Time `json:"created_date"`
ExpiredDate time.Time `json:"expired_date"`
DNSSEC bool `json:"dnssec"`
}
// TianHuRegistrarInfo contains information about the registrar.
type TianHuRegistrarInfo struct {
Key string `json:"key"`
ReferralURL string `json:"referral_url"`
RegistrarName string `json:"registrar_name"`
RegistrarIANAID string `json:"registrar_ianaid"`
RegistrarEmail string `json:"registrar_email"`
RegistrarPhone string `json:"registrar_phone"`
}
// TianHuRegistrantInfo, TianHuAdministrativeInfo, TianHuTechnicalInfo, and TianHuBillingInfo
// can be defined similarly, depending on the details you wish to include.
type TianHuRegistrantInfo struct {
Key string `json:"key"`
// Additional fields can be added here.
}
type TianHuAdministrativeInfo struct {
Key string `json:"key"`
// Additional fields can be added here.
}
type TianHuTechnicalInfo struct {
Key string `json:"key"`
// Additional fields can be added here.
}
type TianHuBillingInfo struct {
Key string `json:"key"`
// Additional fields can be added here.
}
// TianHuWhoisTimezone represents the timezone information of the WHOIS data.
type TianHuWhoisTimezone struct {
UTCOffset int `json:"utcoffset"`
Demo time.Time `json:"demo"`
}

View File

@ -1,14 +0,0 @@
package v1
type WhoisServer struct {
Name string `json:"name" binding:"required"`
Domain string `json:"domain" binding:"required"`
}
// WhocxRequestParams 请求参数结构体
type WhocxRequestParams struct {
Domain string `json:"domain"` // 域名 (必选)
Whois string `json:"whois"` // 域名的whois原始信息 (必选)
Lang string `json:"lang,omitempty"` // 语言代码 (可选)
TimeZone string `json:"time_zone,omitempty"` // 时区 (可选)
}

View File

@ -1,24 +0,0 @@
package main
import (
"fmt"
"go.uber.org/zap"
"whois-go/pkg/config"
"whois-go/pkg/http"
"whois-go/pkg/log"
)
func main() {
conf := config.NewConfig()
logger := log.NewLog(conf)
logger.Info("server start", zap.String("host", "http://127.0.0.1:"+conf.GetString("http.port")))
app, cleanup, err := newApp(conf, logger)
if err != nil {
panic(err)
}
defer cleanup()
http.Run(app, fmt.Sprintf(":%d", conf.GetInt("http.port")))
}

View File

@ -1,43 +0,0 @@
//go:build wireinject
// +build wireinject
package main
import (
"github.com/gin-gonic/gin"
"github.com/google/wire"
"github.com/spf13/viper"
"whois-go/internal/handler"
"whois-go/internal/repository"
"whois-go/internal/server"
"whois-go/internal/service"
"whois-go/pkg/log"
)
var ServerSet = wire.NewSet(server.NewServerHTTP)
var RepositorySet = wire.NewSet(
repository.NewDb,
repository.NewRepository,
)
var ServiceSet = wire.NewSet(
service.NewService,
service.NewUserService,
service.NewSystemService,
)
var HandlerSet = wire.NewSet(
handler.NewHandler,
handler.NewUserHandler,
handler.NewSystemHandler,
)
func newApp(*viper.Viper, *log.Logger) (*gin.Engine, func(), error) {
panic(wire.Build(
ServerSet,
//RepositorySet,
ServiceSet,
HandlerSet,
))
}

View File

@ -1,40 +0,0 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"github.com/gin-gonic/gin"
"github.com/google/wire"
"github.com/spf13/viper"
"whois-go/internal/handler"
"whois-go/internal/repository"
"whois-go/internal/server"
"whois-go/internal/service"
"whois-go/pkg/log"
)
// Injectors from wire.go:
func newApp(viperViper *viper.Viper, logger *log.Logger) (*gin.Engine, func(), error) {
handlerHandler := handler.NewHandler(logger)
serviceService := service.NewService(logger)
systemService := service.NewSystemService(serviceService, logger)
systemHandler := handler.NewSystemHandler(handlerHandler, systemService)
engine := server.NewServerHTTP(logger, systemHandler)
return engine, func() {
}, nil
}
// wire.go:
var ServerSet = wire.NewSet(server.NewServerHTTP)
var RepositorySet = wire.NewSet(repository.NewDb, repository.NewRepository)
var ServiceSet = wire.NewSet(service.NewService, service.NewUserService, service.NewSystemService)
var HandlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler, handler.NewSystemHandler)

View File

@ -1,47 +0,0 @@
[
{
"label": "本地接口",
"name": "nuxt",
"order": 0,
"show": true,
"disabled": false,
"iName": "本地 DNS",
"flag": "material-symbols:dns-outline"
},
{
"label": "Google",
"name": "google",
"order": 1,
"show": true,
"disabled": false,
"iName": "Google",
"flag": "flat-color-icons:google"
},
{
"label": "AliYun",
"name": "aliyun",
"order": 2,
"show": true,
"disabled": false,
"iName": "AliYun",
"flag": "ant-design:aliyun-outlined"
},
{
"label": "Tencent",
"name": "tencent",
"order": 3,
"show": true,
"disabled": false,
"iName": "Tencent",
"flag": "emojione:cloud"
},
{
"label": "Cloudflare",
"name": "cloudflare",
"order": 4,
"show": true,
"disabled": false,
"iName": "CloudFlare",
"flag": "skill-icons:cloudflare-light"
}
]

View File

@ -1,18 +0,0 @@
[
{
"label": "本地接口",
"name": "nuxt",
"order": 0,
"show": false,
"disabled": true,
"iName": "本地 DNS",
"flag": "material-symbols:dns-outline"
},
{
"label": "TIAN.HU",
"name": "tianhu",
"order": 1,
"show": true,
"disabled": false
}
]

View File

@ -1,23 +0,0 @@
[
{
"label": "本地接口",
"name": "nuxt",
"order": 0,
"show": true,
"disabled": false
},
{
"label": "WHO.CX",
"name": "whocx",
"order": 1,
"show": false,
"disabled": true
},
{
"label": "TIAN.HU",
"name": "tianhu",
"order": 2,
"show": true,
"disabled": false
}
]

View File

@ -1,34 +0,0 @@
env: local
http:
port: 8000
#网站配置
login:
username: admin
password: 123456
siteConfig:
logoLeftText: NuxtLeftLogo
logoRightText: NuxtRightLogo
defaultSelectOptions:
- label: Whois
value: whois
- label: Dns
value: dns
- label: Domain
value: domain
whoisSeverApi:
- whocx: https://who.cx/api/whois_extract
- tianhu: https://api.tian.hu/whois.php
dnsSeverApi:
- google: https://dns.google/resolve
- cloudflare: http://1.1.1.1/dns-query
- aliyun: https://223.5.5.5/resolve
- tencent: https://doh.pub/dns-query
log:
log_level: debug
encoding: console # json or console
log_file_name: "./storage/logs/server.log"
max_backups: 30 # 日志文件最多保存多少个备份
max_age: 7 # 文件最多保存多少天
max_size: 1024 # 每个日志文件保存的最大尺寸 单位M
compress: true # 是否压缩

View File

@ -1,27 +0,0 @@
env: local
http:
port: 8000
security:
api_sign:
app_key: 123456
app_security: 123456
jwt:
key: 1234
data:
mysql:
user: root:123456@tcp(127.0.0.1:3380)/user?charset=utf8mb4&parseTime=True&loc=Local
redis:
addr: 127.0.0.1:6350
password: ""
db: 0
read_timeout: 0.2s
write_timeout: 0.2s
log:
log_level: info
encoding: json # json or console
log_file_name: "./storage/logs/server.log"
max_backups: 30 # 日志文件最多保存多少个备份
max_age: 7 # 文件最多保存多少天
max_size: 1024 # 每个日志文件保存的最大尺寸 单位M
compress: true # 是否压缩

View File

@ -1,28 +0,0 @@
module whois-go
go 1.16
require (
github.com/bytedance/sonic v1.11.3 // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/fsnotify/fsnotify v1.7.0
github.com/gin-gonic/gin v1.9.1
github.com/go-playground/validator/v10 v10.19.0 // indirect
github.com/google/uuid v1.4.0
github.com/google/wire v0.6.0
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
github.com/pkg/errors v0.9.1
github.com/sony/sonyflake v1.1.0
github.com/spf13/viper v1.18.2
github.com/ugorji/go/codec v1.2.12 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0
golang.org/x/arch v0.7.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/net v0.23.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/gorm v1.25.9
)

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
package config
type Server struct {
SiteConfig SiteConfig `mapstructure:"siteConfig" json:"siteConfig" yaml:"siteConfig"`
Login LoginConfig `mapstructure:"login" json:"login" yaml:"login"`
}

View File

@ -1,6 +0,0 @@
package config
type LoginConfig struct {
Username string `mapstructure:"username" json:"username" yaml:"username"`
Password string `mapstructure:"password" json:"password" yaml:"password"`
}

View File

@ -1,13 +0,0 @@
package config
type SiteConfig struct {
LogoLeftText string `mapstructure:"logoLeftText" json:"logoLeftText" yaml:"logoLeftText"` // Logo左文字
LogoRightText string `mapstructure:"logoRightText" json:"logoRightText" yaml:"logoRightText"` // logo右文字
DefaultSelectOptions []SelectOptions `mapstructure:"defaultSelectOptions" json:"defaultSelectOptions" yaml:"defaultSelectOptions"` // 默认选择项
WhoisServerApi map[string]string `mapstructure:"whoisSeverApi" json:"whoisSeverApi" yaml:"whoisSeverApi"`
}
type SelectOptions struct {
Label string `json:"label"`
Value string `json:"value"`
}

View File

@ -1,9 +0,0 @@
package global
import "whois-go/internal/config"
var (
// Version is the version of the application.
G_Version = "0.1"
G_CONFIG config.Server
)

View File

@ -1,15 +0,0 @@
package handler
import (
"whois-go/pkg/log"
)
type Handler struct {
logger *log.Logger
}
func NewHandler(logger *log.Logger) *Handler {
return &Handler{
logger: logger,
}
}

View File

@ -1,78 +0,0 @@
package handler
import (
"github.com/gin-gonic/gin"
"net/http"
v1 "whois-go/api/v1"
"whois-go/internal/service"
)
type SystemHandler interface {
GetWebSiteConfig(ctx *gin.Context)
GetWhoisServer(c *gin.Context)
GetWhois(c *gin.Context)
}
type systemHandler struct {
*Handler
systemService service.SystemService
}
func NewSystemHandler(handler *Handler, systemService service.SystemService) SystemHandler {
return &systemHandler{
Handler: handler,
systemService: systemService,
}
}
func (h *systemHandler) GetWebSiteConfig(c *gin.Context) {
config, err := h.systemService.GetSystemConfig()
if err != nil {
v1.FailWithMessage("获取失败", c)
return
}
v1.OkWithDetailed(config, "获取成功", c)
}
func (h *systemHandler) GetWhoisServer(c *gin.Context) {
var fileConfig v1.FileConfig
// 从请求体中绑定JSON数据到configs变量
if err := c.BindJSON(&fileConfig); err != nil {
// 如果解析出错,返回错误信息
v1.FailWithMessage("解析失败", c)
return
}
config, err := h.systemService.GetWhoisJson(fileConfig.Config)
if err != nil {
v1.FailWithMessage("获取失败", c)
return
}
v1.OkWithDetailed(config, "获取成功", c)
}
func (h *systemHandler) GetWhois(c *gin.Context) {
var whoisServer v1.WhoisServer
if err := c.BindJSON(&whoisServer); err != nil {
// 如果解析出错,返回错误信息
v1.FailWithMessage("解析失败", c)
return
}
//判断whoisServer的值来进行不同的函数
switch whoisServer.Name {
case "whocx":
resp := h.systemService.WhoisServerIsWhocx(whoisServer.Domain)
v1.OkWithDetailed(resp, "获取成功", c)
return
case "tianhu":
resp, err := h.systemService.WhoisServerIsTianhu(whoisServer.Domain)
if err != nil {
v1.FailWithMessage("获取失败", c)
return
}
c.JSON(http.StatusOK, resp)
return
default:
v1.FailWithMessage("未知的Whois服务器", c)
return
}
}

View File

@ -1,20 +0,0 @@
package handler
import (
"whois-go/internal/service"
)
type UserHandler interface {
}
type userHandler struct {
*Handler
userService service.UserService
}
func NewUserHandler(handler *Handler, userService service.UserService) UserHandler {
return &userHandler{
Handler: handler,
userService: userService,
}
}

View File

@ -1,23 +0,0 @@
package middleware
import (
"github.com/gin-gonic/gin"
"net/http"
)
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", c.GetHeader("Origin"))
c.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {
c.Header("Access-Control-Allow-Methods", c.GetHeader("Access-Control-Request-Method"))
c.Header("Access-Control-Allow-Headers", c.GetHeader("Access-Control-Request-Headers"))
c.Header("Access-Control-Max-Age", "7200")
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}

View File

@ -1,29 +0,0 @@
package repository
import (
"whois-go/pkg/log"
"gorm.io/gorm"
)
type Repository struct {
db *gorm.DB
//rdb *redis.Client
logger *log.Logger
}
func NewRepository(logger *log.Logger, db *gorm.DB) *Repository {
return &Repository{
db: db,
//rdb: rdb,
logger: logger,
}
}
func NewDb() *gorm.DB {
// TODO: init db
//db, err := gorm.Open(mysql.Open(conf.GetString("data.mysql.user")), &gorm.Config{})
//if err != nil {
// panic(err)
//}
//return db
return &gorm.DB{}
}

View File

@ -1,37 +0,0 @@
package server
import (
"github.com/gin-gonic/gin"
"whois-go/internal/handler"
"whois-go/internal/middleware"
"whois-go/pkg/helper/resp"
"whois-go/pkg/log"
)
func NewServerHTTP(
logger *log.Logger,
systemHandler handler.SystemHandler,
) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
r.Use(
middleware.CORSMiddleware(),
)
r.GET("/", func(ctx *gin.Context) {
resp.HandleSuccess(ctx, map[string]interface{}{
"say": "This is GoLang!",
})
})
// 获取配置文件内容
api := r.Group("/api")
api.POST("/getWebSiteConfig", systemHandler.GetWebSiteConfig)
api.POST("/getWhoisServer", systemHandler.GetWhoisServer)
server := api.Group("/server")
server.POST("/getWhois", systemHandler.GetWhois)
server.POST("/getDns", systemHandler.GetWhois)
return r
}

View File

@ -1,13 +0,0 @@
package service
import "whois-go/pkg/log"
type Service struct {
logger *log.Logger
}
func NewService(logger *log.Logger) *Service {
return &Service{
logger: logger,
}
}

View File

@ -1,94 +0,0 @@
package service
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
v1 "whois-go/api/v1"
"whois-go/internal/config"
"whois-go/internal/global"
"whois-go/internal/utils"
"whois-go/pkg/log"
)
type SystemService interface {
GetSystemConfig() (config.SiteConfig, error)
GetWhoisJson(fileConfig string) ([]v1.WhoisApiInterface, error)
WhoisServerIsWhocx(domain string) string
WhoisServerIsTianhu(domain string) (v1.TianHuWhoisResponse, error)
}
func NewSystemService(service *Service, logger *log.Logger) SystemService {
return &systemService{
Service: service,
logger: logger,
}
}
type systemService struct {
*Service
logger *log.Logger
}
// GetSystemConfig 读取配置文件
func (s *systemService) GetSystemConfig() (config.SiteConfig, error) {
return global.G_CONFIG.SiteConfig, nil
}
func (s *systemService) GetWhoisJson(fileConfig string) ([]v1.WhoisApiInterface, error) {
file, err := os.Open(fmt.Sprintf("config/json/%s.json", fileConfig))
if err != nil {
s.logger.Fatal(fmt.Sprintf("Error opening file: %s", err))
}
defer file.Close()
// 读取文件内容
bytes, err := ioutil.ReadAll(file)
// 反序列化JSON到结构体切片
var apis []v1.WhoisApiInterface
if err = json.Unmarshal(bytes, &apis); err != nil {
s.logger.Fatal(fmt.Sprintf("Error unmarshalling JSON: %s", err))
}
return apis, err
}
func (s *systemService) WhoisServerIsWhocx(domain string) string {
whocxURL := global.G_CONFIG.SiteConfig.WhoisServerApi["whocx"]
params := url.Values{}
params.Set("domain", domain)
params.Set("whois", "Domain Name: ")
params.Set("lang", "zh")
params.Set("time_zone", "8")
request, err := utils.SendPostRequest(whocxURL, params)
if err != nil {
return ""
}
return request
}
func (s *systemService) WhoisServerIsTianhu(domain string) (v1.TianHuWhoisResponse, error) {
tianhuURL := global.G_CONFIG.SiteConfig.WhoisServerApi["tianhu"]
params := url.Values{}
//domain=baidu.com&action=searchWhois
params.Set("domain", domain)
params.Set("action", "searchWhois")
var tianhuResp v1.TianHuWhoisResponse
// Append query parameters to the URL
fullURL := tianhuURL + "?" + params.Encode()
// Perform the GET request
response, err := http.Get(fullURL)
defer response.Body.Close()
// Read the response body
body, err := ioutil.ReadAll(response.Body)
err = json.Unmarshal(body, &tianhuResp)
return tianhuResp, err
}

View File

@ -1,18 +0,0 @@
package service
type UserService interface {
}
type userService struct {
*Service
}
func NewUserService(service *Service) UserService {
return &userService{
Service: service,
}
}
func (s *userService) GetUserById(id int64) {
}

View File

@ -1,55 +0,0 @@
package utils
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
// FetchData 从给定的URL获取数据
func FetchData(url string) ([]byte, error) {
// 发起GET请求
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应体失败: %v", err)
}
return body, nil
}
// SendPostRequest 发送POST请求的函数
func SendPostRequest(requestURL string, params url.Values) (string, error) {
// 创建POST请求
req, err := http.NewRequest("POST", requestURL, strings.NewReader(params.Encode()))
if err != nil {
return "", fmt.Errorf("创建请求失败: %v", err)
}
// 设置请求头,表明请求体是表单数据
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("发送请求失败: %v", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应体失败: %v", err)
}
return string(body), nil
}

View File

@ -1,44 +0,0 @@
package config
import (
"flag"
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"os"
"whois-go/internal/global"
)
func NewConfig() *viper.Viper {
envConf := os.Getenv("APP_CONF")
if envConf == "" {
flag.StringVar(&envConf, "conf", "config/local.yml", "config path, eg: -conf config/local.yml")
flag.Parse()
}
if envConf == "" {
envConf = "config/local.yml"
}
fmt.Println("load conf file:", envConf)
return getConfig(envConf)
}
func getConfig(path string) *viper.Viper {
conf := viper.New()
conf.SetConfigFile(path)
err := conf.ReadInConfig()
if err != nil {
panic(err)
}
conf.WatchConfig()
conf.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("config file changed:", e.Name)
if err = conf.Unmarshal(&global.G_CONFIG); err != nil {
fmt.Println(err)
}
})
if err = conf.Unmarshal(&global.G_CONFIG); err != nil {
panic(err)
}
return conf
}

View File

@ -1,24 +0,0 @@
package convert
const (
base62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
func IntToBase62(n int) string {
if n == 0 {
return string(base62[0])
}
var result []byte
for n > 0 {
result = append(result, base62[n%62])
n /= 62
}
// 反转字符串
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
result[i], result[j] = result[j], result[i]
}
return string(result)
}

View File

@ -1,11 +0,0 @@
package md5
import (
"crypto/md5"
"encoding/hex"
)
func Md5(str string) string {
hash := md5.Sum([]byte(str))
return hex.EncodeToString(hash[:])
}

View File

@ -1,28 +0,0 @@
package resp
import (
"github.com/gin-gonic/gin"
"net/http"
)
type response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func HandleSuccess(ctx *gin.Context, data interface{}) {
if data == nil {
data = map[string]string{}
}
resp := response{Code: 0, Message: "success", Data: data}
ctx.JSON(http.StatusOK, resp)
}
func HandleError(ctx *gin.Context, httpCode, code int, message string, data interface{}) {
if data == nil {
data = map[string]string{}
}
resp := response{Code: code, Message: message, Data: data}
ctx.JSON(httpCode, resp)
}

View File

@ -1,32 +0,0 @@
package sid
import (
"whois-go/pkg/helper/convert"
"github.com/pkg/errors"
"github.com/sony/sonyflake"
)
type Sid struct {
sf *sonyflake.Sonyflake
}
func NewSid() *Sid {
sf := sonyflake.NewSonyflake(sonyflake.Settings{})
if sf == nil {
panic("sonyflake not created")
}
return &Sid{sf}
}
func (s Sid) GenString() (string, error) {
// 生成分布式ID
id, err := s.sf.NextID()
if err != nil {
return "", errors.Wrap(err, "failed to generate sonyflake ID")
}
// 将ID转换为字符串
return convert.IntToBase62(int(id)), nil
}
func (s Sid) GenUint64() (uint64, error) {
// 生成分布式ID
return s.sf.NextID()
}

View File

@ -1,7 +0,0 @@
package uuid
import "github.com/google/uuid"
func GenUUID() string {
return uuid.NewString()
}

View File

@ -1,48 +0,0 @@
package http
import (
"context"
"github.com/gin-gonic/gin"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func Run(r *gin.Engine, addr string) {
srv := &http.Server{
Addr: addr,
Handler: r,
}
// Initializing the server in a goroutine so that
// it won't block the graceful shutdown handling below
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
// The context is used to inform the server it has 5 seconds to finish
// the request it is currently handling
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown: ", err)
}
log.Println("Server exiting")
}

View File

@ -1,115 +0,0 @@
package log
import (
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"os"
"time"
)
const LOGGER_KEY = "zapLogger"
type Logger struct {
*zap.Logger
}
func NewLog(conf *viper.Viper) *Logger {
return initZap(conf)
}
func initZap(conf *viper.Viper) *Logger {
// 日志地址 "out.log" 自定义
lp := conf.GetString("log.log_file_name")
// 日志级别 DEBUG,ERROR, INFO
lv := conf.GetString("log.log_level")
var level zapcore.Level
//debug<info<warn<error<fatal<panic
switch lv {
case "debug":
level = zap.DebugLevel
case "info":
level = zap.InfoLevel
case "warn":
level = zap.WarnLevel
case "error":
level = zap.ErrorLevel
default:
level = zap.InfoLevel
}
hook := lumberjack.Logger{
Filename: lp, // 日志文件路径
MaxSize: conf.GetInt("log.max_size"), // 每个日志文件保存的最大尺寸 单位M
MaxBackups: conf.GetInt("log.max_backups"), // 日志文件最多保存多少个备份
MaxAge: conf.GetInt("log.max_age"), // 文件最多保存多少天
Compress: conf.GetBool("log.compress"), // 是否压缩
}
var encoder zapcore.Encoder
if conf.GetString("log.encoding") == "console" {
encoder = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "Logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseColorLevelEncoder,
EncodeTime: timeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
})
} else {
encoder = zapcore.NewJSONEncoder(zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.EpochTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
})
}
core := zapcore.NewCore(
encoder, // 编码器配置
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 打印到控制台和文件
level, // 日志级别
)
if conf.GetString("env") != "prod" {
return &Logger{zap.New(core, zap.Development(), zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
}
return &Logger{zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))}
}
// 自定义时间编码器
func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
//enc.AppendString(t.Format("2006-01-02 15:04:05"))
enc.AppendString(t.Format("2006-01-02 15:04:05.000000000"))
}
// NewContext 给指定的context添加字段
func (l *Logger) NewContext(ctx *gin.Context, fields ...zapcore.Field) {
ctx.Set(LOGGER_KEY, l.WithContext(ctx).With(fields...))
}
// WithContext 从指定的context返回一个zap实例
func (l *Logger) WithContext(ctx *gin.Context) *Logger {
if ctx == nil {
return l
}
zl, _ := ctx.Get(LOGGER_KEY)
ctxLogger, ok := zl.(*zap.Logger)
if ok {
return &Logger{ctxLogger}
}
return l
}

View File

@ -1,5 +1,7 @@
<script setup lang="ts">
const domainListStore = useDomainListStore();
const SupportedTLDs = domainListStore.getSupportedTLDKeys
const isOpen = ref(false)
const {t} = useI18n()
@ -26,13 +28,13 @@ const {t} = useI18n()
closable
>
<div class="flex flex-wrap mt-2 ">
<!-- <span-->
<!-- v-for="item in SupportedTLDs"-->
<!-- :key="item"-->
<!-- class="m-1 px-2 py-1 text-sm font-semibold text-gray-800 bg-gray-200 rounded hover:bg-gray-300"-->
<!-- >-->
<!-- {{ item }}-->
<!-- </span>-->
<span
v-for="item in SupportedTLDs"
:key="item"
class="m-1 px-2 py-1 text-sm font-semibold text-gray-800 bg-gray-200 rounded hover:bg-gray-300"
>
{{ item }}
</span>
</div>
</NDrawerContent>
</NDrawer>

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
const isOpen = ref(false)
const settingsStore = useSettingsStore()
const styleStore = useStyleStore()
const {t} = useI18n()
</script>
@ -33,12 +33,12 @@ const {t} = useI18n()
<h1 class="text-2xl font-bold text-gray-800 mb-5 flex items-center justify-between">
<span
class="text-sm text-gray-500 py-1 px-3 rounded-full dark:text-white dark:bg-[#000000FF]">
{{ t('history.tips', {length: settingsStore.getHistory.length}) }}
{{ t('history.tips', {length: styleStore.getHistory.length}) }}
</span>
</h1>
<div class=" shadow-md rounded-lg ">
<!-- 条件渲染如果有历史记录则显示表格否则显示提示 -->
<div v-if="settingsStore.getHistory.length">
<div v-if="styleStore.getHistory.length">
<!-- 表格头部和内容 -->
<table class="min-w-full leading-normal ">
<thead>
@ -59,7 +59,7 @@ const {t} = useI18n()
</thead>
<tbody>
<!-- 这里将使用循环来动态展示查询历史 -->
<tr v-for="item in settingsStore.getHistory" :key="item.id"
<tr v-for="item in styleStore.getHistory" :key="item.id"
class="border-b border-gray-200 dark:text-white dark:bg-[#000000FF]">
<td class="px-5 py-5 text-sm ">
<NuxtLink :to="item.path">{{ item.domain }}</NuxtLink>
@ -72,7 +72,7 @@ const {t} = useI18n()
</td>
<td class="px-5 py-5 text-sm ">
<NButton
@click="settingsStore.deleteHistory(item.id)"
@click="styleStore.deleteHistory(item.id)"
>{{ t('common.actions.delete') }}
</NButton>

View File

@ -0,0 +1,84 @@
<script lang="ts" setup>
import {useTimeStore} from "~/stores/time";
const availableTimeZones = ref([
{ id: 1, name: 'UTC-12', displayName: 'International Date Line West' },
{ id: 2, name: 'UTC-11', displayName: 'Coordinated Universal Time-11' },
{ id: 3, name: 'UTC-10', displayName: 'Hawaii' },
{ id: 4, name: 'UTC-9', displayName: 'Alaska' },
{ id: 5, name: 'UTC-8', displayName: 'Pacific Time (US & Canada)' },
{ id: 6, name: 'UTC-7', displayName: 'Mountain Time (US & Canada)' },
{ id: 7, name: 'UTC-6', displayName: 'Central Time (US & Canada), Mexico City' },
{ id: 8, name: 'UTC-5', displayName: 'Eastern Time (US & Canada), Bogota, Lima' },
{ id: 9, name: 'UTC-4', displayName: 'Atlantic Time (Canada), Caracas, La Paz' },
{ id: 10, name: 'UTC-3', displayName: 'Buenos Aires, Georgetown' },
{ id: 11, name: 'UTC-2', displayName: 'Coordinated Universal Time-02' },
{ id: 12, name: 'UTC-1', displayName: 'Azores' },
{ id: 13, name: 'UTC', displayName: 'Coordinated Universal Time' },
{ id: 14, name: 'UTC+1', displayName: 'Brussels, Copenhagen, Madrid, Paris' },
{ id: 15, name: 'UTC+2', displayName: 'Athens, Bucharest, Istanbul' },
{ id: 16, name: 'UTC+3', displayName: 'Moscow, St. Petersburg, Nairobi' },
{ id: 17, name: 'UTC+3:30', displayName: 'Tehran' },
{ id: 18, name: 'UTC+4', displayName: 'Abu Dhabi, Muscat' },
{ id: 19, name: 'UTC+4:30', displayName: 'Kabul' },
{ id: 20, name: 'UTC+5', displayName: 'Islamabad, Karachi, Tashkent' },
{ id: 21, name: 'UTC+5:30', displayName: 'Chennai, Kolkata, Mumbai, New Delhi' },
{ id: 22, name: 'UTC+5:45', displayName: 'Kathmandu' },
{ id: 23, name: 'UTC+6', displayName: 'Astana, Dhaka' },
{ id: 24, name: 'UTC+6:30', displayName: 'Yangon (Rangoon)' },
{ id: 25, name: 'UTC+7', displayName: 'Bangkok, Hanoi, Jakarta' },
{ id: 26, name: 'UTC+8', displayName: 'Beijing, Hong Kong, Singapore, Taipei' },
{ id: 27, name: 'UTC+9', displayName: 'Osaka, Sapporo, Tokyo' },
{ id: 28, name: 'UTC+9:30', displayName: 'Adelaide, Darwin' },
{ id: 29, name: 'UTC+10', displayName: 'Brisbane, Canberra, Melbourne, Sydney' },
{ id: 30, name: 'UTC+11', displayName: 'Solomon Is., New Caledonia' },
{ id: 31, name: 'UTC+12', displayName: 'Auckland, Wellington' },
{ id: 32, name: 'UTC+13', displayName: 'Nuku alofa' }
]);
const timeStore = useTimeStore()
</script>
<template>
<div>
<HeadlessListbox
v-model="timeStore.timeZones"
as="div"
class="relative flex items-center"
>
<HeadlessListboxLabel class="sr-only">
Theme
</HeadlessListboxLabel>
<HeadlessListboxButton type="button" title="Change Color">
<div
class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700"
>
<Icon name="ri:time-zone-line" class=" text-lg dark:text-white" />
</div>
</HeadlessListboxButton>
<HeadlessListboxOptions
class="absolute top-full right-0 z-[999] mt-2 w-40 max-h-60 overflow-y-auto rounded-lg bg-white text-sm font-semibold text-gray-700 shadow-lg shadow-gray-300 outline-none dark:bg-gray-800 dark:text-white dark:shadow-gray-500 dark:ring-0"
>
<HeadlessListboxOption
v-for="item in availableTimeZones"
:key="item.id"
:value="item.name"
class="flex w-full cursor-pointer items-center justify-between py-2 px-3 hover:bg-gray-100 dark:hover:bg-gray-600"
:class="{
'text-white-500 bg-gray-200 dark:bg-gray-500/50':
timeStore.timeZones === item.name,
'hover:bg-gray-200 dark:hover:bg-gray-700/30':
timeStore.timeZones !== item.name,
}"
>
<span class="truncate">
{{ item.name }}
</span>
<span class="flex items-center justify-center text-sm">
</span>
</HeadlessListboxOption>
</HeadlessListboxOptions>
</HeadlessListbox>
</div>
</template>

View File

@ -1,12 +1,14 @@
<script lang="ts" setup>
const switchLocalePath = useSwitchLocalePath()
const timeStore = useTimeStore()
const settingsStore = useSettingsStore()
const dnsStore = useDnsStore()
const configStore = useConfigStore()
const {configServer} = storeToRefs(configStore)
const {newsDnsArr} = storeToRefs(dnsStore)
const handlePost = async (name: string) => {
dnsStore.moveToTop(name)
//
await refreshNuxtData('dns')
}
@ -16,7 +18,7 @@ const handlePost = async (name: string) => {
<template>
<div>
<HeadlessListbox
v-model="configServer.dnsArr"
v-model="dnsStore.newsDnsArr"
as="div"
class="relative flex items-center"
>
@ -35,7 +37,7 @@ const handlePost = async (name: string) => {
class="absolute top-full right-0 z-[999] mt-2 w-40 overflow-hidden rounded-lg bg-white text-sm font-semibold text-gray-700 shadow-lg shadow-gray-300 outline-none dark:bg-gray-800 dark:text-white dark:shadow-gray-500 dark:ring-0"
>
<div
v-for="lang in configServer.dnsArr"
v-for="lang in newsDnsArr"
:key="lang.name"
@click="handlePost(lang.name)"
:class="{

View File

@ -0,0 +1,43 @@
<script setup lang="ts">
defineProps({
data: {
type: Object,
required: true
}
})
</script>
<template>
<div class="flex bg-gray-100 p-8">
<div class="w-full mx-auto">
<div class="bg-white shadow-lg rounded-lg p-6 mb-4" v-for="item in data.Answer">
<h2 class="text-xl font-bold">{{ item.name }}</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<p class="font-semibold">Type</p>
<p>{{ item.type }}</p>
</div>
<div>
<p class="font-semibold">TTL</p>
<p>{{ item.TTL }}</p>
</div>
<div class="col-span-2">
<p class="font-semibold">Data</p>
<p>{{ item.data }}</p>
</div>
</div>
<div class="flex space-x-2 mt-4">
<span class="bg-green-200 text-green-800 px-2 py-1 rounded">RD: {{ data.RD }}</span>
<span class="bg-green-200 text-green-800 px-2 py-1 rounded">RA: {{ data.RA }}</span>
<span class="bg-red-200 text-red-800 px-2 py-1 rounded">TC: {{ data.TC }}</span>
<span class="bg-red-200 text-red-800 px-2 py-1 rounded">AD: {{ data.AD }}</span>
<span class="bg-red-200 text-red-800 px-2 py-1 rounded">CD: {{ data.CD }}</span>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
const timeStore = useTimeStore()
const settingsStore = useSettingsStore()
const {getIsDomainList, getIsHistory,} = storeToRefs(settingsStore)
const {t} = useI18n()
</script>
@ -10,7 +10,7 @@ const {t} = useI18n()
<div class="flex space-x-2">
<!-- 左边的新元素 -->
<n-tooltip
v-if="getIsDomainList"
v-if="settingsStore.isDomainList"
trigger="hover" placement="top">
<template #trigger>
<CommonDomainList/>
@ -19,7 +19,7 @@ const {t} = useI18n()
</n-tooltip>
<n-tooltip
v-if="getIsHistory"
v-if="settingsStore.getHistory"
trigger="hover" placement="top">
<template #trigger>
<CommonHistory/>
@ -27,6 +27,15 @@ const {t} = useI18n()
<span>{{ t('popper.history') }}</span>
</n-tooltip>
<!-- <n-tooltip-->
<!-- v-if="settingsStore.getHistory"-->
<!-- trigger="hover" placement="top" >-->
<!-- <template #trigger>-->
<!-- <CommonDnsList @action="handleActionFromDnsList" />-->
<!-- </template>-->
<!-- <span>{{t('popper.dns')}}</span>-->
<!-- </n-tooltip>-->
</div>
<div class="flex space-x-2">
<!-- 右边的现有元素 -->
@ -54,7 +63,7 @@ const {t} = useI18n()
<template #trigger>
<CommonTimeZonesChange/>
</template>
<span>{{ settingsStore.timeZones }}</span>
<span>{{ timeStore.timeZones }}</span>
</n-tooltip>
<n-tooltip

View File

@ -0,0 +1,43 @@
export const trimDomain = (domain: string): string => {
return domain.trim().toLowerCase(); // 确保域名为小写
};
export const splitDomain = (domain: string): string[] => {
return domain.split('.');
};
//
// const SupportedTLDs = new Set(Object.keys(domainStore.SupportedTLDs));
//
// export const updateDomainForTLD = (parts: string[]): string => {
// const potentialTLD = parts.slice(-2).join('.').toLowerCase(); // 确保为小写
// let domainToKeep: string;
// if (SupportedTLDs.has(potentialTLD)) {
// domainToKeep = parts.length > 2 ? parts.slice(-3).join('.') : parts.join('.');
// } else {
// domainToKeep = parts.slice(-2).join('.');
// }
// return domainToKeep;
// };
//
//
// export const validateDomain = (parts: string[]): boolean => {
// if (parts.length < 2) {
// const message = useMessage()
// message.warning('域名格式不正确')
// return false;
// }
// return true;
// };
//
//
// const isTLDValid = (parts: string[]): boolean => {
// const lastPart = parts[parts.length - 1].toLowerCase(); // 获取最后一部分,并确保为小写
// const potentialTLD = parts.slice(-2).join('.').toLowerCase(); // 获取可能的多部分TLD并确保为小写
//
// if (!SupportedTLDs.has(lastPart) && !SupportedTLDs.has(potentialTLD)) {
// const message = useMessage()
// message.warning('域名后缀不合法')
// return false;
// }
// return true;
// };

View File

@ -1,12 +0,0 @@
enum Api {
blogInfo = '/',
report = '/report'
}
/**
*
* @returns
*/
export function getBlogInfo(option: any) {
return ""
}

View File

@ -1,23 +0,0 @@
enum Api {
webSiteConfig = '/getWebSiteConfig',
whoisServer = "/getWhoisServer"
}
// 获取网站配置
export function GetWebSiteConfig() {
return useHttp.post<WebSiteConfig>("/getWebSiteConfig")
}
//获取whois服务器
export function GetWhoisServer(config: string) {
return useHttp.post<any>("/getWhoisServer", {
config: config,
})
}
export function GetWhois(domain: string, name: string) {
return useHttp.post<any>("/getWhois", {
domain: domain,
name: name,
})
}

View File

@ -1,5 +0,0 @@
// api/front/index.ts
import * as home from './home';
// Re-export home
export {home};

View File

@ -1,2 +0,0 @@
export * as admin from './admin/index';
export * as front from './front/index';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

View File

@ -1,82 +0,0 @@
/**********************************
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/05 21:26:28
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
#app {
width: 100%;
height: 100%;
}
/* transition fade-slide */
.fade-slide-leave-active,
.fade-slide-enter-active {
transition: all 0.3s;
}
.fade-slide-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-slide-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* 自定义滚动条样式 */
.cus-scroll {
overflow: auto;
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
}
.cus-scroll-x {
overflow-x: auto;
&::-webkit-scrollbar {
width: 0;
height: 8px;
}
}
.cus-scroll-y {
overflow-y: auto;
&::-webkit-scrollbar {
width: 8px;
height: 0;
}
}
.cus-scroll,
.cus-scroll-x,
.cus-scroll-y {
&::-webkit-scrollbar-thumb {
background-color: transparent;
border-radius: 4px;
}
&:hover {
&::-webkit-scrollbar-thumb {
background: #bfbfbf;
}
&::-webkit-scrollbar-thumb:hover {
background: var(--primary-color);
}
}
}

View File

@ -1,49 +0,0 @@
/**********************************
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/05 21:26:38
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
html {
box-sizing: border-box;
}
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: inherit;
}
a {
text-decoration: none;
color: inherit;
}
a:hover,
a:link,
a:visited,
a:active {
text-decoration: none;
}
ol,
ul {
list-style: none;
}
input,
textarea {
outline: none;
border: none;
resize: none;
}
img,
video {
max-width: 100%;
height: auto;
}

View File

@ -1,82 +0,0 @@
<script lang="ts" setup>
const availableTimeZones = ref([
{id: 1, name: 'UTC-12', displayName: 'International Date Line West'},
{id: 2, name: 'UTC-11', displayName: 'Coordinated Universal Time-11'},
{id: 3, name: 'UTC-10', displayName: 'Hawaii'},
{id: 4, name: 'UTC-9', displayName: 'Alaska'},
{id: 5, name: 'UTC-8', displayName: 'Pacific Time (US & Canada)'},
{id: 6, name: 'UTC-7', displayName: 'Mountain Time (US & Canada)'},
{id: 7, name: 'UTC-6', displayName: 'Central Time (US & Canada), Mexico City'},
{id: 8, name: 'UTC-5', displayName: 'Eastern Time (US & Canada), Bogota, Lima'},
{id: 9, name: 'UTC-4', displayName: 'Atlantic Time (Canada), Caracas, La Paz'},
{id: 10, name: 'UTC-3', displayName: 'Buenos Aires, Georgetown'},
{id: 11, name: 'UTC-2', displayName: 'Coordinated Universal Time-02'},
{id: 12, name: 'UTC-1', displayName: 'Azores'},
{id: 13, name: 'UTC', displayName: 'Coordinated Universal Time'},
{id: 14, name: 'UTC+1', displayName: 'Brussels, Copenhagen, Madrid, Paris'},
{id: 15, name: 'UTC+2', displayName: 'Athens, Bucharest, Istanbul'},
{id: 16, name: 'UTC+3', displayName: 'Moscow, St. Petersburg, Nairobi'},
{id: 17, name: 'UTC+3:30', displayName: 'Tehran'},
{id: 18, name: 'UTC+4', displayName: 'Abu Dhabi, Muscat'},
{id: 19, name: 'UTC+4:30', displayName: 'Kabul'},
{id: 20, name: 'UTC+5', displayName: 'Islamabad, Karachi, Tashkent'},
{id: 21, name: 'UTC+5:30', displayName: 'Chennai, Kolkata, Mumbai, New Delhi'},
{id: 22, name: 'UTC+5:45', displayName: 'Kathmandu'},
{id: 23, name: 'UTC+6', displayName: 'Astana, Dhaka'},
{id: 24, name: 'UTC+6:30', displayName: 'Yangon (Rangoon)'},
{id: 25, name: 'UTC+7', displayName: 'Bangkok, Hanoi, Jakarta'},
{id: 26, name: 'UTC+8', displayName: 'Beijing, Hong Kong, Singapore, Taipei'},
{id: 27, name: 'UTC+9', displayName: 'Osaka, Sapporo, Tokyo'},
{id: 28, name: 'UTC+9:30', displayName: 'Adelaide, Darwin'},
{id: 29, name: 'UTC+10', displayName: 'Brisbane, Canberra, Melbourne, Sydney'},
{id: 30, name: 'UTC+11', displayName: 'Solomon Is., New Caledonia'},
{id: 31, name: 'UTC+12', displayName: 'Auckland, Wellington'},
{id: 32, name: 'UTC+13', displayName: 'Nuku alofa'}
]);
const settingsStore = useSettingsStore()
</script>
<template>
<div>
<HeadlessListbox
v-model="settingsStore.timeZones"
as="div"
class="relative flex items-center"
>
<HeadlessListboxLabel class="sr-only">
Theme
</HeadlessListboxLabel>
<HeadlessListboxButton type="button" title="Change Color">
<div
class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700"
>
<Icon name="ri:time-zone-line" class=" text-lg dark:text-white"/>
</div>
</HeadlessListboxButton>
<HeadlessListboxOptions
class="absolute top-full right-0 z-[999] mt-2 w-40 max-h-60 overflow-y-auto rounded-lg bg-white text-sm font-semibold text-gray-700 shadow-lg shadow-gray-300 outline-none dark:bg-gray-800 dark:text-white dark:shadow-gray-500 dark:ring-0"
>
<HeadlessListboxOption
v-for="item in availableTimeZones"
:key="item.id"
:value="item.name"
class="flex w-full cursor-pointer items-center justify-between py-2 px-3 hover:bg-gray-100 dark:hover:bg-gray-600"
:class="{
'text-white-500 bg-gray-200 dark:bg-gray-500/50':
settingsStore.timeZones === item.name,
'hover:bg-gray-200 dark:hover:bg-gray-700/30':
settingsStore.timeZones !== item.name,
}"
>
<span class="truncate">
{{ item.name }}
</span>
<span class="flex items-center justify-center text-sm">
</span>
</HeadlessListboxOption>
</HeadlessListboxOptions>
</HeadlessListbox>
</div>
</template>

View File

@ -1,43 +0,0 @@
<script setup lang="ts">
defineProps({
data: {
type: Object,
required: true
}
})
</script>
<template>
<div class="flex p-8">
<div class="w-full mx-auto">
<div class=" shadow-lg rounded-lg p-6 mb-4" v-for="item in data.Answer">
<h2 class="text-xl font-bold">{{ item.name }}</h2>
<div class="grid grid-cols-2 gap-4">
<div>
<p class="font-semibold">Type</p>
<p>{{ item.type }}</p>
</div>
<div>
<p class="font-semibold">TTL</p>
<p>{{ item.TTL }}</p>
</div>
<div class="col-span-2">
<p class="font-semibold">Data</p>
<p>{{ item.data }}</p>
</div>
</div>
<div class="flex space-x-2 mt-4">
<span class="bg-green-200 text-green-800 px-2 py-1 rounded">RD: {{ data.RD }}</span>
<span class="bg-green-200 text-green-800 px-2 py-1 rounded">RA: {{ data.RA }}</span>
<span class="bg-red-200 text-red-800 px-2 py-1 rounded">TC: {{ data.TC }}</span>
<span class="bg-red-200 text-red-800 px-2 py-1 rounded">AD: {{ data.AD }}</span>
<span class="bg-red-200 text-red-800 px-2 py-1 rounded">CD: {{ data.CD }}</span>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -1,13 +0,0 @@
<script setup lang="ts">
const props = defineProps({
data: Object,
})
</script>
<template>
{{ data }}
</template>
<style scoped>
</style>

View File

@ -1,13 +0,0 @@
<script setup lang="ts">
const props = defineProps({
data: Object,
})
</script>
<template>
{{ data }}
</template>
<style scoped>
</style>

View File

@ -1,26 +0,0 @@
// composables/useCurrentServer.js 或者在一个组件中
export const useCurrentServer = () => {
// 使用useHydration
useHydration('currentServer',
// 服务端执行的逻辑,返回初始值
() => {
return {
currentServer: {
whois: 'nuxt',
dns: 'nuxt',
domain: 'nuxt',
}
};
},
// 客户端执行的逻辑,接收服务端设置的值
(serverValue) => {
// 在客户端处理或使用服务端传来的值
console.log('Received from server:', serverValue);
// 这里你可以根据需要将serverValue设置到Pinia store或Vue响应式状态中
// 例如更新Pinia store中的状态
const configStore = useConfigStore();
configStore.setCurrentServer(serverValue);
}
);
}

View File

@ -1,12 +0,0 @@
// 自动导出
export const useHttp = {
post<T = any>(url: string, config?: any): Promise<T> {
const runtimeConfig = useRuntimeConfig()
const baseUrl = runtimeConfig.public.baseUrl
return $fetch(url, {
baseURL: baseUrl,
method: 'POST',
body: config,
})
},
}

View File

@ -1,112 +0,0 @@
<script setup lang="ts">
import {type MenuOption, NIcon} from "naive-ui";
import type {Component} from "vue";
import {Settings, Wrench, Pentagon, MessageCircleMore} from 'lucide-vue-next';
function renderIcon(icon: Component) {
return () => h(NIcon, null, {default: () => h(icon)})
}
const route = useRoute()
const router = useRouter()
const menusOptions: MenuOption[] = [
{
label: '控制台',
key: '/admin/dashboard',
icon: renderIcon(Pentagon)
},
{
label: 'Whois管理',
key: 'whois',
icon: renderIcon(Settings),
children: [
{
label: '后缀管理',
key: '/admin/whois/website',
icon: renderIcon(Wrench),
}
]
},
{
label: 'Dns管理',
key: 'dns',
icon: renderIcon(Settings),
children: [
{
label: '后缀管理',
key: '/admin/whois/website',
icon: renderIcon(Wrench),
}
]
},
{
label: 'Domain管理',
key: 'domain',
icon: renderIcon(Settings),
children: [
{
label: '后缀管理',
key: '/admin/whois/website',
icon: renderIcon(Wrench),
}
]
},
{
label: '系统管理',
key: 'settings',
icon: renderIcon(Settings),
children: [
{
label: '网站设置',
key: '/admin/settings/website',
icon: renderIcon(Wrench),
},
{
label: '公告管理',
key: '/admin/settings/bulletin',
icon: renderIcon(MessageCircleMore),
}
]
}
]
const activeKey = computed(() => route.path)
console.log(route.path)
function handleMenuSelect(key: any, item: any) {
if (isExternal(item.key)) {
router.push(item.key)
} else {
router.push(item.key)
}
}
</script>
<template>
<div class="flex h-screen bg-gray-100">
<!-- Left Sidebar -->
<div class="w-64 text-black">
<n-menu
ref="menu"
class="side-menu"
accordion
:indent="18"
:collapsed-icon-size="22"
:collapsed-width="64"
:options="menusOptions"
:value="activeKey"
@update:value="handleMenuSelect"
/>
</div>
<!-- Right Content -->
<div class="flex-1 p-10">
<slot/>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -1,132 +0,0 @@
<script setup lang="ts">
const route = useRoute()
const router = useRouter();
const {t} = useI18n()
const localePath = useLocalePath()
const message = useMessage()
const settingsStore = useSettingsStore()
const {isObj, domainSearch, selectedOption, webSiteConfig} = storeToRefs(settingsStore)
//
const stylePage = computed(() => {
return route.meta?.stylePage
})
//
const handleAction = async (url) => {
const etDomainSearch = ExtractDomain(domainSearch.value);
switch (url) {
case 'whois':
// 使
if (DomainRegex.test(etDomainSearch)) {
//
domainSearch.value = etDomainSearch;
//
const urlType = etDomainSearch.replace(/\./g, '_');
await router.push(localePath(`/whois/${urlType}.html`));
} else if (Ipv4Regex.test(domainSearch.value)) {
//
const urlType = domainSearch.value.replace(/\./g, '_');
await router.push(localePath(`/whois/${urlType}.html`));
} else {
message.error('请输入有效的域名或IP地址')
return;
}
break;
case 'dns':
// 使
if (DomainRegex.test(etDomainSearch)) {
//
domainSearch.value = etDomainSearch;
//
const urlType = etDomainSearch.replace(/\./g, '_');
await router.push(localePath(`/dns/${urlType}.html`));
} else if (Ipv4Regex.test(domainSearch.value)) {
//
const urlType = domainSearch.value.replace(/\./g, '_');
await router.push(localePath(`/dns/${urlType}.html`));
} else {
message.error('请输入有效的域名或IP地址')
return;
}
return
}
}
const handleSelectOptions = (value: any) => {
settingsStore.setSelectedOption(value);
}
</script>
<template>
<div
class="w-full text-xs bg-[#F1F3F4] dark:bg-transparent"
:class="{ 'h-[90vh]': !stylePage }"
>
<div
class=" max-w-screen-lg mx-auto px-[1em] pb-[10vh] "
:class="{ 'pt-[25vh]': !stylePage, 'pt-[5vh]': stylePage }"
>
<nav
v-if="isObj.isLogo"
class=" w-full text-[#464747] h-5 ">
<NuxtLink class="mb-3 font-bold text-2xl inline-block text-current no-underline dark:text-white"
:to="localePath('/')"
>
<h1 class="inline-block text-current no-underline dark:text-white">{{ webSiteConfig.logoLeftText }}</h1>
<sup class="text-[#59a8d7] dark:text-[#ace4f8]">{{ webSiteConfig.logoRightText }}</sup>
</NuxtLink>
</nav>
<div class="mt-6">
<ClientOnly>
<div class="flex items-center space-x-2 mb-3 dark:text-white"
>
<!-- 容器div用于水平布局 -->
<div class="flex-grow">
<NInputGroup>
<n-select
class="w-1/4"
size="large"
v-model:value="selectedOption"
:options="webSiteConfig.defaultSelectOptions"
@update:value="handleSelectOptions"
/>
<NAutoComplete
v-model:value="domainSearch"
@keyup.enter="handleAction(settingsStore.selectedOption)"
:input-props="{ autocomplete: 'off' }"
type="text"
:placeholder="t('index.placeholder')"
size="large"
clearable
autofocus
class="w-full ">
</NAutoComplete>
</NInputGroup>
</div>
<!-- 使用v-if基于state.domain的值来控制按钮的显示 -->
<NButton type="primary"
size="large"
@click="handleAction(settingsStore.selectedOption)"
v-if="settingsStore.domainSearch">
{{ t('index.onSubmit') }}
</NButton>
</div>
</ClientOnly>
</div>
<CommonBulletin
v-if="!stylePage && isObj.isBulletin"
:text="`➡️ ${t('index.tips') }`"
/>
<TabList @action="handleAction"/>
<slot/>
</div>
</div>
<CommonFooter/>
</template>
<style scoped>
</style>

View File

@ -1,15 +0,0 @@
<script setup lang="ts">
definePageMeta({
layout: "admin",
})
</script>
<template>
123
</template>
<style scoped>
</style>

View File

@ -1,99 +0,0 @@
<template>
<div class="login-container flex items-center justify-center p-[20px]">
<div class="login-box w-[960px] h-[560px] md:w-[100%] md:px-[50px] px-[80px] py-[30px]">
<div class="flex items-center justify-center md:hidden">
<img :src="LoginPic" alt="login-pic"/>
</div>
<div class="flex justify-center w-[360px] md:w-[100%] flex-col items-center space-y-8">
<div class="space-y-2">
<div class="flex justify-center items-center space-x-[10px]">
<n-gradient-text :gradient="gradient" class="text-[24px] p-5">Nuxt Whois 后台管理</n-gradient-text>
</div>
</div>
<n-form
ref="formRef"
:model="formModel"
:rules="formRules"
label-placement="left"
size="large"
class="w-[100%]"
:show-require-mark="false"
:show-label="false"
>
<n-form-item path="username">
<n-input v-model:value="formModel.username" round placeholder="请输入用户名">
<template #prefix>
<Icon name="mdi:user-outline"/>
</template>
</n-input>
</n-form-item>
<n-form-item path="password">
<n-input v-model:value="formModel.password" round placeholder="请输入密码">
<template #prefix>
<Icon name="material-symbols:lock-outline"/>
</template>
</n-input>
</n-form-item>
<n-form-item>
<div class="flex justify-between w-[100%] px-[2px]">
<div class="flex-initial">
<n-checkbox v-model:checked="autoLogin">自动登录</n-checkbox>
</div>
<div class="flex-initial order-last">
<n-button text type="primary">忘记密码</n-button>
</div>
</div>
</n-form-item>
<n-form-item>
<n-button type="primary" size="large" round @click="handleSubmitForm" :loading="submitLoading" block>登录
</n-button>
</n-form-item>
</n-form>
</div>
</div>
</div>
</template>
<script setup>
definePageMeta({
layout: "empty",
})
import LoginPic from "assets/images/login-pic.png";
const router = useRouter();
const message = useMessage();
const autoLogin = ref(false);
const submitLoading = ref(false);
const formRef = ref(null);
const formModel = reactive({
username: "",
password: "",
});
const formRules = {
username: {required: true, message: "请输入用户名", trigger: "blur"},
password: {required: true, message: "请输入密码", trigger: "blur"},
};
const gradient = {
deg: 92.06,
from: "#33c2ff 0%",
// to: `${appStore.appTheme} 100%`,
};
const handleSubmitForm = (e) => {
};
</script>
<style lang="less" scoped>
.login-container {
background: linear-gradient(-135deg, #d765cf, #495fd1);
min-height: 100%;
.login-box {
@apply shadow-md rounded-xl bg-white flex justify-between;
}
}
</style>

View File

@ -1,13 +0,0 @@
<script setup lang="ts">
definePageMeta({
layout: "full",
})
</script>
<template>
</template>
<style scoped>
</style>

View File

@ -1,62 +0,0 @@
<script setup lang="ts">
definePageMeta({
layout: "full",
})
const siteName = ref('');
const siteNameAlt = ref('');
function saveSettings() {
// API
console.log('保存的网站名称:', siteName.value);
}
const active = ref(false);
const GnOptions = ref([
{
label: 'Whois',
value: 'whois'
}, {
label: 'Dns',
value: 'dns'
}, {
label: 'Domain',
value: 'domain'
}
])
const GnValue = ref('whois')
</script>
<template>
<div class="bg-gray-100 p-10">
<div class=" bg-white rounded-lg shadow-md p-5">
<h2 class="text-xl font-semibold mb-5">网站设置</h2>
<div class="flex mb-6 -mx-2">
<div class="flex-1 px-2">
<label for="site-name" class="block mb-2 text-sm font-medium text-gray-900">网站Logo名称</label>
<n-input id="site-name" v-model:value="siteName" placeholder="请输入网站名称"/>
</div>
<div class="flex-1 px-2">
<label for="site-name-alt" class="block mb-2 text-sm font-medium text-gray-900">Logo右上角名称</label>
<n-input id="site-name-alt" v-model:value="siteNameAlt" placeholder="请输入备用网站名称"/>
</div>
</div>
<div class="flex mb-6 -mx-2">
<div class="flex-1 px-2">
<label for="site-name" class="block mb-2 text-sm font-medium text-gray-900">开启极简模式</label>
<n-switch v-model:value="active"/>
</div>
<div class="flex-1 px-2">
<label for="site-name-alt" class="block mb-2 text-sm font-medium text-gray-900">功能开启选择</label>
<n-select v-model:value="GnValue" multiple :options="GnOptions"/>
</div>
</div>
<div class="flex justify-end">
<n-button type="primary" @click="saveSettings">保存设置</n-button>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -1,218 +0,0 @@
<script setup lang="ts">
definePageMeta({
stylePage: true,
})
const settingsStore = useSettingsStore()
const {t} = useI18n()
const {isObj} = storeToRefs(settingsStore)
const handleReset = async () => {
}
const styleStore = useStyleStore();
const localCommon = ref({...styleStore.common});
const modalVisible = ref(false)
const swatches = ['#FFFFFF', '#18A058', '#2080F0', '#F0A020', 'rgba(208, 48, 80, 1)'];
const updateColors = () => {
styleStore.updatePrimaryColor(localCommon.value);
};
</script>
<template>
<ClientOnly>
<div class="setting mt-8 settings-grid">
<n-h6 prefix="bar"> 基础设置</n-h6>
<n-card class="set-item">
<div class="top grid grid-cols-2 gap-4">
<div class="name">
<n-text class="text">{{ t('settings.title') }}</n-text>
<n-text class="tip" depth="3">{{ t('settings.history') }}</n-text>
</div>
<n-switch v-model:value="isObj.isHistory" :round="false"/>
<div class="name">
<n-text class="text">公告设置</n-text>
<n-text class="tip" depth="3">是否开启首页公告功能</n-text>
</div>
<n-switch v-model:value="isObj.isBulletin" :round="false"/>
<div class="name">
<n-text class="text">支持列表</n-text>
<n-text class="tip" depth="3">是否开启支持列表功能</n-text>
</div>
<n-switch v-model:value="isObj.isDomainList" :round="false"/>
<div class="name">
<n-text class="text">支持列表</n-text>
<n-text class="tip" depth="3">是否显示 Logo</n-text>
</div>
<n-switch v-model:value="isObj.isLogo" :round="false"/>
</div>
</n-card>
<n-modal v-model:show="modalVisible" title="颜色设置" :style="{ width: '480px' }">
<div class="p-8">
<div class="max-w-4xl mx-auto">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div>
<div class="text-sm font-semibold mb-2">Primary Color</div>
<n-color-picker v-model:value="localCommon.primaryColor" :swatches="swatches"/>
</div>
<div>
<div class="text-sm font-semibold mb-2">Info Color</div>
<n-color-picker v-model:value="localCommon.infoColor" :swatches="swatches"/>
</div>
<!-- 重复上述结构以添加更多颜色选择器 -->
<div>
<div class="text-sm font-semibold mb-2">Success Color</div>
<n-color-picker v-model:value="localCommon.successColor" :swatches="swatches"/>
</div>
<div>
<div class="text-sm font-semibold mb-2">Warning Color</div>
<n-color-picker v-model:value="localCommon.warningColor" :swatches="swatches"/>
</div>
<div>
<div class="text-sm font-semibold mb-2">Error Color</div>
<n-color-picker v-model:value="localCommon.errorColor" :swatches="swatches"/>
</div>
</div>
<n-button @click="updateColors" class="mt-6">Update Colors</n-button>
</div>
</div>
</n-modal>
<div class="mx-auto mt-10">
<n-h6 class="mb-4 text-lg font-bold text-gray-800" prefix="bar">颜色设置</n-h6>
<n-card class="set-item p-4 shadow-lg">
<div class="top flex justify-between items-center">
<div class="name">
<n-text class="text text-gray-600">自定义所有颜色</n-text>
</div>
<n-button type="warning" @click="modalVisible = true" class="bg-orange-500 hover:bg-orange-600">确认
</n-button>
</div>
</n-card>
</div>
<n-h6 prefix="bar"> 杂项设置</n-h6>
<n-card class="set-item">
<div class="top">
<div class="name">
<n-text class="text">重置所有数据</n-text>
<n-text class="tip" depth="3">
重置所有数据你的自定义设置都将会丢失
</n-text>
</div>
<n-popconfirm
@positive-click="handleReset"
:negative-text="t('common.actions.cancel')"
:positive-text="t('common.actions.confirm')"
>
<template #trigger>
<n-button type="warning"> {{ t('common.actions.reset') }}</n-button>
</template>
确认重置所有数据你的自定义设置都将会丢失
</n-popconfirm>
</div>
</n-card>
</div>
</ClientOnly>
</template>
<style scoped>
.setting {
.title {
margin-top: 30px;
margin-bottom: 20px;
font-size: 40px;
font-weight: bold;
}
.n-h {
padding-left: 16px;
font-size: 20px;
margin-left: 4px;
}
.set-item {
width: 100%;
border-radius: 8px;
margin-bottom: 12px;
.top {
display: flex;
align-items: center;
justify-content: space-between;
.name {
font-size: 18px;
display: flex;
flex-direction: column;
.tip {
font-size: 12px;
border-radius: 8px;
}
}
.set {
max-width: 200px;
}
}
.mews-group {
margin-top: 16px;
display: grid;
grid-template-columns: repeat(5, minmax(0px, 1fr));
gap: 24px;
@media (max-width: 1666px) {
grid-template-columns: repeat(4, minmax(0px, 1fr));
}
@media (max-width: 1200px) {
grid-template-columns: repeat(3, minmax(0px, 1fr));
}
@media (max-width: 890px) {
grid-template-columns: repeat(2, minmax(0px, 1fr));
}
@media (max-width: 620px) {
grid-template-columns: repeat(1, minmax(0px, 1fr));
}
.item {
cursor: pointer;
.desc {
display: flex;
align-items: center;
width: 100%;
transition: all 0.3s;
.logo {
width: 40px;
height: 40px;
margin-right: 12px;
}
.news-name {
font-size: 16px;
}
}
.switch {
margin-left: auto;
}
}
}
}
}
</style>

View File

@ -1,71 +0,0 @@
<script setup lang="ts">
import {useCurrentServer} from "~/composables/useCurrentServer";
definePageMeta({
stylePage: true,
})
const {t} = useI18n()
const route = useRoute();
const {domain}: any = route.params;
const domainData = typeof domain === "string" ? domain?.replace(/_/g, '.') : "";
const settingsStore = useSettingsStore()
const localePath = useLocalePath()
const configStore = useConfigStore()
const {currentServer} = storeToRefs(configStore)
const {data, pending, error, refresh} = await useAsyncData(
'whois',
() => $fetch('/api/server/whois', {
method: 'POST',
body: {
domain: domainData,
serverName: currentServer.value.whois,
},
})
)
if (!error.value && settingsStore.getIsHistory) {
settingsStore.addOrUpdateHistory(
{
id: domainData,
type: 'whois',
domain: domainData,
path: localePath(`/whois/${domain}.html`),
date: AdjustTimeToUTCOffset(new Date().toString(), settingsStore.timeZones)
}
)
}
useHead({
title: `${domainData} - ${t('whois.title')}`,
meta: [
{
name: 'description',
content: t('whois.description', {domain: domainData})
}, {
name: 'keywords',
content: t('whois.keywords', {domain: domainData})
}
]
})
</script>
<template>
<div
class="w-full bg-[#fffffe] mt-5 p-4 shadow-lg rounded-lg whitespace-pre-wrap dark:text-gray-200 dark:bg-gray-800"
>
<WhoisNuxt
v-if="currentServer.whois == 'nuxt'"
:data="data"
/>
<WhoisTianHu
v-if="currentServer.whois == 'tianhu'"
:data="data"
/>
</div>
</template>
<style scoped>
</style>

View File

@ -1,15 +0,0 @@
let theme = localStorage.getItem("nuxt-color-mode");
function setTheme(theme) {
if (theme === "system" || !theme) {
theme =
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
}
document.querySelector("html").classList.add(theme);
document.documentElement.setAttribute("class", theme);
}
setTheme(theme);

View File

@ -1,24 +0,0 @@
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const domain = body.domain;
const serverName = body.serverName;
if (serverName == '') {
return ""
}
const runtimeConfig = useRuntimeConfig()
const baseUrl = runtimeConfig.public.baseUrl
switch (serverName) {
case "nuxt": {
return ""
}
default:
return await $fetch(`${baseUrl}/server/getDns`, {
method: 'POST',
body: {
name: serverName,
domain: domain,
}
})
}
});

View File

@ -1,25 +0,0 @@
import {whois} from "~/server/whois/whois";
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const serverName = body.serverName;
const domain = body.domain
if (serverName == '') {
return ""
}
const runtimeConfig = useRuntimeConfig()
const baseUrl = runtimeConfig.public.baseUrl
switch (serverName) {
case "nuxt": {
return await whois(domain)
}
default:
return await $fetch(`${baseUrl}/server/getWhois`, {
method: 'POST',
body: {
name: serverName,
domain: domain,
}
})
}
})

View File

@ -1,66 +0,0 @@
import {defineStore} from 'pinia'
import {front} from "~/apis/index";
export const useConfigStore = defineStore('useConfig', {
state: () => {
return {
configServer: {
whoisArr: [] as any,
dnsArr: [] as any,
domainArr: [] as any,
},
currentServer: {
whois: 'nuxt',
dns: 'nuxt',
domain: 'nuxt',
}
}
},
actions: {
async configServerInit() {
// 获取网站whois设置
try {
const {data: whoisArray} = await front.home.GetWhoisServer('whois')
this.configServer.whoisArr = whoisArray
} catch (e) {
this.configServer.whoisArr = []
}
// 获取网站dns设置
try {
const {data: dnsArray} = await front.home.GetWhoisServer('dns')
this.configServer.dnsArr = dnsArray
} catch (e) {
this.configServer.dnsArr = []
}
// 获取网站域名设置
try {
const {data: domainArray} = await front.home.GetWhoisServer('domain')
this.configServer.domainArr = domainArray
} catch (e) {
this.configServer.domainArr = []
}
},
setCurrentServer(configServer: any) {
this.currentServer = configServer
},
setCurrentServerWhois(whois: any) {
this.currentServer.whois = whois
},
setCurrentServerDns(dns: any) {
this.currentServer.dns = dns
},
setCurrenServerDomain(domain: any) {
this.currentServer.domain = domain
}
},
getters: {
// 获取所有的 Whois 服务器
getConfigServer: (state: any) => {
return state.configServer
},
},
persist: {
storage: persistedState.localStorage,
},
})

View File

@ -1,119 +0,0 @@
import {defineStore} from 'pinia'
import {front} from "~/apis/index";
interface HistoryRecord {
id: number;
type: string;
domain: string;
path: string;
date: string;
}
export const useSettingsStore = defineStore('settings', {
state: () => {
return {
webSiteConfig: {} as WebSiteConfig,
isObj: {
isHistory: false, // 是否显示历史记录
isBulletin: false, // 是否显示公告
isDomainList: false, // 是否显示域名列表
isLogo: true, // 是否显示logo
},
histories: [] as HistoryRecord[], //页面模式下的历史记录
selectedOption: 'whois', // 默认查询类型
domainSearch: '', // 域名搜索
timeZones: 'UTC+8', // 时区
}
},
actions: {
async webSiteConfigInit() {
try {
// @ts-ignore
const {data: webData} = await front.home.GetWebSiteConfig()
this.webSiteConfig = webData;
} catch (e) {
this.webSiteConfig = {} as WebSiteConfig;
}
},
// 设置历史记录
setHistories(histories: any) {
this.histories = histories
},
// 添加或更新历史记录
addOrUpdateHistory(newHistory: {
date: string;
path: any;
domain: any;
id: any;
type: string
}) {
const existingIndex = this.histories.findIndex(
history =>
history.domain === newHistory.domain
&& history.type === newHistory.type);
if (existingIndex !== -1) {
// 更新存在的记录的时间戳
this.histories[existingIndex].date = new Date().toISOString();
} else {
// 添加新记录之前,检查是否已达到保存记录的最大数量
if (this.histories.length >= 30) {
// 确保历史记录按时间降序排列
this.histories.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
// 移除最旧的记录
this.histories.pop();
}
// 添加新的记录
const record: HistoryRecord = {
...newHistory,
id: Date.now(),
date: new Date().toISOString(),
};
this.histories.unshift(record); // 添加到数组的开头
}
// 再次确保历史记录按时间降序排列
this.histories.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
},
deleteHistory(id: number) {
const index = this.histories.findIndex(history => history.id === id);
if (index !== -1) {
this.histories.splice(index, 1);
}
},
setSelectedOption(name: string) {
this.selectedOption = name;
},
setTimeZones(timeZones: string) {
this.timeZones = timeZones
},
},
getters: {
//是否显示历史记录
getIsDomainList: (state: any) => state.isObj.isDomainList,
//是否显示公告
getIsBulletin: (state: any) => state.isObj.isBulletin,
// 是否显示历史记录
getIsHistory: (state: any) => state.isObj.isHistory,
// 是否显示logo
getIsLogo: (state: any) => state.isObj.isLogo,
// 获取历史记录
getHistories(state) {
return state.histories
},
// 获取上次搜索的记录
getDomain(state: any) {
return state.domainSearch;
},
// 获取时区
getTimeZones(state) {
return state.timeZones
},
},
persist: {
// 持久化存储到 Cookie 中
storage: persistedState.cookiesWithOptions({
sameSite: 'strict',
}),
},
})

Some files were not shown because too many files have changed in this diff Show More