Compare commits
No commits in common. "main" and "next" have entirely different histories.
0
frontend/.gitignore → .gitignore
vendored
0
frontend/.gitignore → .gitignore
vendored
@ -12,7 +12,6 @@ Nuxt-Whois 是一个基于 Nuxt3、Tailwind CSS 和 Xep-Whois 构建的Whois查
|
|||||||
|
|
||||||
## 更新说明
|
## 更新说明
|
||||||
|
|
||||||
- 2024.4.7 增加 Go 后端 完善后台管理 增加用户自定义主题色 (目前基础合并,三天后会提交正式,禁止同步当前)
|
|
||||||
- 2024.3.25 抛弃原来 NuxtUi 改用 NaiveUi 重构中 后台增加中 当前版本无法上线使用
|
- 2024.3.25 抛弃原来 NuxtUi 改用 NaiveUi 重构中 后台增加中 当前版本无法上线使用
|
||||||
- 2024.3.18 重构V2版本 预计三天内完成。
|
- 2024.3.18 重构V2版本 预计三天内完成。
|
||||||
|
|
||||||
|
27
app.config.ts
Normal file
27
app.config.ts
Normal 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],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -11,6 +11,7 @@
|
|||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<NuxtLoadingIndicator/>
|
<NuxtLoadingIndicator/>
|
||||||
<NuxtPage/>
|
<NuxtPage/>
|
||||||
|
<!-- <CommonLayoutSetting v-if="isAdminRoute" class="fixed right-12 top-1/2 z-999" />-->
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</n-message-provider>
|
</n-message-provider>
|
||||||
</n-modal-provider>
|
</n-modal-provider>
|
||||||
@ -18,26 +19,21 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {darkTheme, lightTheme} from 'naive-ui'
|
import {darkTheme, lightTheme} from 'naive-ui'
|
||||||
|
import {naiveThemeOverrides} from "~/settings/settings";
|
||||||
|
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
const themeOverrides = naiveThemeOverrides
|
||||||
|
|
||||||
const theme = computed(() => {
|
const theme = computed(() => {
|
||||||
return colorMode.value === 'system' ? (colorMode.value ? lightTheme : darkTheme) : colorMode.value === 'light' ? lightTheme : darkTheme
|
return colorMode.value === 'system' ? (colorMode.value ? lightTheme : darkTheme) : colorMode.value === 'light' ? lightTheme : darkTheme
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleStore = useStyleStore()
|
const whoisStore = useWhoisStore()
|
||||||
const {common} = storeToRefs(styleStore)
|
const dnsStore = useDnsStore()
|
||||||
const themeOverrides = computed(() => {
|
const domainStore = useDomainStore()
|
||||||
return {
|
whoisStore.newWhoisList()
|
||||||
common: common.value, // 注意这里要使用 common.value
|
dnsStore.newDnsList()
|
||||||
}
|
domainStore.newDomainList()
|
||||||
})
|
|
||||||
|
|
||||||
await callOnce(async () => {
|
|
||||||
await useSettingsStore().webSiteConfigInit()
|
|
||||||
await useConfigStore().configServerInit()
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
3
backend/.gitignore
vendored
3
backend/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/storage/
|
|
||||||
.idea
|
|
||||||
*.log
|
|
@ -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.
|
|
@ -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
|
|
||||||
|
|
||||||
```
|
|
@ -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"`
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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"`
|
|
||||||
}
|
|
@ -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"` // 时区 (可选)
|
|
||||||
}
|
|
@ -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")))
|
|
||||||
}
|
|
@ -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,
|
|
||||||
))
|
|
||||||
}
|
|
@ -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)
|
|
@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
@ -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
|
|
||||||
}
|
|
||||||
]
|
|
@ -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
|
|
||||||
}
|
|
||||||
]
|
|
@ -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 # 是否压缩
|
|
@ -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 # 是否压缩
|
|
@ -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
|
|
||||||
)
|
|
2592
backend/go.sum
2592
backend/go.sum
File diff suppressed because it is too large
Load Diff
@ -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"`
|
|
||||||
}
|
|
@ -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"`
|
|
||||||
}
|
|
@ -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"`
|
|
||||||
}
|
|
@ -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
|
|
||||||
)
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -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{}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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) {
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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[:])
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package uuid
|
|
||||||
|
|
||||||
import "github.com/google/uuid"
|
|
||||||
|
|
||||||
func GenUUID() string {
|
|
||||||
return uuid.NewString()
|
|
||||||
}
|
|
@ -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")
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -1,5 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
const domainListStore = useDomainListStore();
|
||||||
|
const SupportedTLDs = domainListStore.getSupportedTLDKeys
|
||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
|
|
||||||
@ -26,13 +28,13 @@ const {t} = useI18n()
|
|||||||
closable
|
closable
|
||||||
>
|
>
|
||||||
<div class="flex flex-wrap mt-2 ">
|
<div class="flex flex-wrap mt-2 ">
|
||||||
<!-- <span-->
|
<span
|
||||||
<!-- v-for="item in SupportedTLDs"-->
|
v-for="item in SupportedTLDs"
|
||||||
<!-- :key="item"-->
|
:key="item"
|
||||||
<!-- class="m-1 px-2 py-1 text-sm font-semibold text-gray-800 bg-gray-200 rounded hover:bg-gray-300"-->
|
class="m-1 px-2 py-1 text-sm font-semibold text-gray-800 bg-gray-200 rounded hover:bg-gray-300"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- {{ item }}-->
|
{{ item }}
|
||||||
<!-- </span>-->
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</NDrawerContent>
|
</NDrawerContent>
|
||||||
</NDrawer>
|
</NDrawer>
|
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const settingsStore = useSettingsStore()
|
const styleStore = useStyleStore()
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -33,12 +33,12 @@ const {t} = useI18n()
|
|||||||
<h1 class="text-2xl font-bold text-gray-800 mb-5 flex items-center justify-between">
|
<h1 class="text-2xl font-bold text-gray-800 mb-5 flex items-center justify-between">
|
||||||
<span
|
<span
|
||||||
class="text-sm text-gray-500 py-1 px-3 rounded-full dark:text-white dark:bg-[#000000FF]">
|
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>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div class=" shadow-md rounded-lg ">
|
<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 ">
|
<table class="min-w-full leading-normal ">
|
||||||
<thead>
|
<thead>
|
||||||
@ -59,7 +59,7 @@ const {t} = useI18n()
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<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]">
|
class="border-b border-gray-200 dark:text-white dark:bg-[#000000FF]">
|
||||||
<td class="px-5 py-5 text-sm ">
|
<td class="px-5 py-5 text-sm ">
|
||||||
<NuxtLink :to="item.path">{{ item.domain }}</NuxtLink>
|
<NuxtLink :to="item.path">{{ item.domain }}</NuxtLink>
|
||||||
@ -72,7 +72,7 @@ const {t} = useI18n()
|
|||||||
</td>
|
</td>
|
||||||
<td class="px-5 py-5 text-sm ">
|
<td class="px-5 py-5 text-sm ">
|
||||||
<NButton
|
<NButton
|
||||||
@click="settingsStore.deleteHistory(item.id)"
|
@click="styleStore.deleteHistory(item.id)"
|
||||||
|
|
||||||
>{{ t('common.actions.delete') }}
|
>{{ t('common.actions.delete') }}
|
||||||
</NButton>
|
</NButton>
|
84
components/common/TimeZonesChange.vue
Normal file
84
components/common/TimeZonesChange.vue
Normal 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>
|
@ -1,12 +1,14 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const switchLocalePath = useSwitchLocalePath()
|
const switchLocalePath = useSwitchLocalePath()
|
||||||
|
const timeStore = useTimeStore()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
|
const dnsStore = useDnsStore()
|
||||||
|
|
||||||
const configStore = useConfigStore()
|
const {newsDnsArr} = storeToRefs(dnsStore)
|
||||||
const {configServer} = storeToRefs(configStore)
|
|
||||||
|
|
||||||
const handlePost = async (name: string) => {
|
const handlePost = async (name: string) => {
|
||||||
|
dnsStore.moveToTop(name)
|
||||||
//刷新数据
|
//刷新数据
|
||||||
await refreshNuxtData('dns')
|
await refreshNuxtData('dns')
|
||||||
}
|
}
|
||||||
@ -16,7 +18,7 @@ const handlePost = async (name: string) => {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<HeadlessListbox
|
<HeadlessListbox
|
||||||
v-model="configServer.dnsArr"
|
v-model="dnsStore.newsDnsArr"
|
||||||
as="div"
|
as="div"
|
||||||
class="relative flex items-center"
|
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"
|
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
|
<div
|
||||||
v-for="lang in configServer.dnsArr"
|
v-for="lang in newsDnsArr"
|
||||||
:key="lang.name"
|
:key="lang.name"
|
||||||
@click="handlePost(lang.name)"
|
@click="handlePost(lang.name)"
|
||||||
:class="{
|
:class="{
|
43
components/dns/InfoList.vue
Normal file
43
components/dns/InfoList.vue
Normal 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>
|
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const timeStore = useTimeStore()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
const {getIsDomainList, getIsHistory,} = storeToRefs(settingsStore)
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ const {t} = useI18n()
|
|||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<!-- 左边的新元素 -->
|
<!-- 左边的新元素 -->
|
||||||
<n-tooltip
|
<n-tooltip
|
||||||
v-if="getIsDomainList"
|
v-if="settingsStore.isDomainList"
|
||||||
trigger="hover" placement="top">
|
trigger="hover" placement="top">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<CommonDomainList/>
|
<CommonDomainList/>
|
||||||
@ -19,7 +19,7 @@ const {t} = useI18n()
|
|||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
|
|
||||||
<n-tooltip
|
<n-tooltip
|
||||||
v-if="getIsHistory"
|
v-if="settingsStore.getHistory"
|
||||||
trigger="hover" placement="top">
|
trigger="hover" placement="top">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<CommonHistory/>
|
<CommonHistory/>
|
||||||
@ -27,6 +27,15 @@ const {t} = useI18n()
|
|||||||
<span>{{ t('popper.history') }}</span>
|
<span>{{ t('popper.history') }}</span>
|
||||||
</n-tooltip>
|
</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>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<!-- 右边的现有元素 -->
|
<!-- 右边的现有元素 -->
|
||||||
@ -54,7 +63,7 @@ const {t} = useI18n()
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<CommonTimeZonesChange/>
|
<CommonTimeZonesChange/>
|
||||||
</template>
|
</template>
|
||||||
<span>{{ settingsStore.timeZones }}</span>
|
<span>{{ timeStore.timeZones }}</span>
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
|
|
||||||
<n-tooltip
|
<n-tooltip
|
43
composables/DomainFormat.ts
Normal file
43
composables/DomainFormat.ts
Normal 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;
|
||||||
|
// };
|
@ -1,12 +0,0 @@
|
|||||||
enum Api {
|
|
||||||
blogInfo = '/',
|
|
||||||
report = '/report'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取博客信息
|
|
||||||
* @returns 博客信息
|
|
||||||
*/
|
|
||||||
export function getBlogInfo(option: any) {
|
|
||||||
return ""
|
|
||||||
}
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
// api/front/index.ts
|
|
||||||
import * as home from './home';
|
|
||||||
|
|
||||||
// Re-export home
|
|
||||||
export {home};
|
|
@ -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 |
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,13 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const props = defineProps({
|
|
||||||
data: Object,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
{{ data }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,13 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const props = defineProps({
|
|
||||||
data: Object,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
{{ data }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -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);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
@ -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,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,15 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
definePageMeta({
|
|
||||||
layout: "admin",
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
123
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -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>
|
|
@ -1,13 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
definePageMeta({
|
|
||||||
layout: "full",
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
|
@ -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,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
@ -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,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
@ -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,
|
|
||||||
},
|
|
||||||
})
|
|
@ -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
Loading…
x
Reference in New Issue
Block a user