diff --git a/.devdbrc b/.devdbrc new file mode 100644 index 0000000..fda8502 --- /dev/null +++ b/.devdbrc @@ -0,0 +1,6 @@ +[ + { + "type": "sqlite", + "path": "/projects/douyin-proxy/data.db" + } +] \ No newline at end of file diff --git a/.gen/model/device.go b/.gen/model/device.go new file mode 100644 index 0000000..46a91d4 --- /dev/null +++ b/.gen/model/device.go @@ -0,0 +1,17 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type Device struct { + ID int32 `sql:"primary_key"` + UUID string + Name string + Expert string + State string + Note string +} diff --git a/.gen/model/expert.go b/.gen/model/expert.go new file mode 100644 index 0000000..d808caa --- /dev/null +++ b/.gen/model/expert.go @@ -0,0 +1,21 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type Expert struct { + ID int32 `sql:"primary_key"` + UID string + SecUID string + ShortID string + RealName string + NickName string + State string + Since int32 + Config string + ConfigAt int32 +} diff --git a/.gen/model/follower.go b/.gen/model/follower.go new file mode 100644 index 0000000..741d3d2 --- /dev/null +++ b/.gen/model/follower.go @@ -0,0 +1,21 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type Follower struct { + ID int32 `sql:"primary_key"` + Avatar string + Nickname string + SecUID string + ShortID string + UID string + UniqueID string + ExpertUID string + Followed int32 + CreatedAt int32 +} diff --git a/.gen/table/device.go b/.gen/table/device.go new file mode 100644 index 0000000..4143c26 --- /dev/null +++ b/.gen/table/device.go @@ -0,0 +1,90 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var Device = newDeviceTable("", "device", "") + +type deviceTable struct { + sqlite.Table + + // Columns + ID sqlite.ColumnInteger + UUID sqlite.ColumnString + Name sqlite.ColumnString + Expert sqlite.ColumnString + State sqlite.ColumnString + Note sqlite.ColumnString + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type DeviceTable struct { + deviceTable + + EXCLUDED deviceTable +} + +// AS creates new DeviceTable with assigned alias +func (a DeviceTable) AS(alias string) *DeviceTable { + return newDeviceTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new DeviceTable with assigned schema name +func (a DeviceTable) FromSchema(schemaName string) *DeviceTable { + return newDeviceTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new DeviceTable with assigned table prefix +func (a DeviceTable) WithPrefix(prefix string) *DeviceTable { + return newDeviceTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new DeviceTable with assigned table suffix +func (a DeviceTable) WithSuffix(suffix string) *DeviceTable { + return newDeviceTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newDeviceTable(schemaName, tableName, alias string) *DeviceTable { + return &DeviceTable{ + deviceTable: newDeviceTableImpl(schemaName, tableName, alias), + EXCLUDED: newDeviceTableImpl("", "excluded", ""), + } +} + +func newDeviceTableImpl(schemaName, tableName, alias string) deviceTable { + var ( + IDColumn = sqlite.IntegerColumn("id") + UUIDColumn = sqlite.StringColumn("uuid") + NameColumn = sqlite.StringColumn("name") + ExpertColumn = sqlite.StringColumn("expert") + StateColumn = sqlite.StringColumn("state") + NoteColumn = sqlite.StringColumn("note") + allColumns = sqlite.ColumnList{IDColumn, UUIDColumn, NameColumn, ExpertColumn, StateColumn, NoteColumn} + mutableColumns = sqlite.ColumnList{UUIDColumn, NameColumn, ExpertColumn, StateColumn, NoteColumn} + ) + + return deviceTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + ID: IDColumn, + UUID: UUIDColumn, + Name: NameColumn, + Expert: ExpertColumn, + State: StateColumn, + Note: NoteColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/.gen/table/expert.go b/.gen/table/expert.go new file mode 100644 index 0000000..902e536 --- /dev/null +++ b/.gen/table/expert.go @@ -0,0 +1,102 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var Expert = newExpertTable("", "expert", "") + +type expertTable struct { + sqlite.Table + + // Columns + ID sqlite.ColumnInteger + UID sqlite.ColumnString + SecUID sqlite.ColumnString + ShortID sqlite.ColumnString + RealName sqlite.ColumnString + NickName sqlite.ColumnString + State sqlite.ColumnString + Since sqlite.ColumnInteger + Config sqlite.ColumnString + ConfigAt sqlite.ColumnInteger + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type ExpertTable struct { + expertTable + + EXCLUDED expertTable +} + +// AS creates new ExpertTable with assigned alias +func (a ExpertTable) AS(alias string) *ExpertTable { + return newExpertTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ExpertTable with assigned schema name +func (a ExpertTable) FromSchema(schemaName string) *ExpertTable { + return newExpertTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new ExpertTable with assigned table prefix +func (a ExpertTable) WithPrefix(prefix string) *ExpertTable { + return newExpertTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new ExpertTable with assigned table suffix +func (a ExpertTable) WithSuffix(suffix string) *ExpertTable { + return newExpertTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newExpertTable(schemaName, tableName, alias string) *ExpertTable { + return &ExpertTable{ + expertTable: newExpertTableImpl(schemaName, tableName, alias), + EXCLUDED: newExpertTableImpl("", "excluded", ""), + } +} + +func newExpertTableImpl(schemaName, tableName, alias string) expertTable { + var ( + IDColumn = sqlite.IntegerColumn("id") + UIDColumn = sqlite.StringColumn("uid") + SecUIDColumn = sqlite.StringColumn("sec_uid") + ShortIDColumn = sqlite.StringColumn("short_id") + RealNameColumn = sqlite.StringColumn("real_name") + NickNameColumn = sqlite.StringColumn("nick_name") + StateColumn = sqlite.StringColumn("state") + SinceColumn = sqlite.IntegerColumn("since") + ConfigColumn = sqlite.StringColumn("config") + ConfigAtColumn = sqlite.IntegerColumn("config_at") + allColumns = sqlite.ColumnList{IDColumn, UIDColumn, SecUIDColumn, ShortIDColumn, RealNameColumn, NickNameColumn, StateColumn, SinceColumn, ConfigColumn, ConfigAtColumn} + mutableColumns = sqlite.ColumnList{UIDColumn, SecUIDColumn, ShortIDColumn, RealNameColumn, NickNameColumn, StateColumn, SinceColumn, ConfigColumn, ConfigAtColumn} + ) + + return expertTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + ID: IDColumn, + UID: UIDColumn, + SecUID: SecUIDColumn, + ShortID: ShortIDColumn, + RealName: RealNameColumn, + NickName: NickNameColumn, + State: StateColumn, + Since: SinceColumn, + Config: ConfigColumn, + ConfigAt: ConfigAtColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/.gen/table/follower.go b/.gen/table/follower.go new file mode 100644 index 0000000..b23fd2a --- /dev/null +++ b/.gen/table/follower.go @@ -0,0 +1,102 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var Follower = newFollowerTable("", "follower", "") + +type followerTable struct { + sqlite.Table + + // Columns + ID sqlite.ColumnInteger + Avatar sqlite.ColumnString + Nickname sqlite.ColumnString + SecUID sqlite.ColumnString + ShortID sqlite.ColumnString + UID sqlite.ColumnString + UniqueID sqlite.ColumnString + ExpertUID sqlite.ColumnString + Followed sqlite.ColumnInteger + CreatedAt sqlite.ColumnInteger + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type FollowerTable struct { + followerTable + + EXCLUDED followerTable +} + +// AS creates new FollowerTable with assigned alias +func (a FollowerTable) AS(alias string) *FollowerTable { + return newFollowerTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new FollowerTable with assigned schema name +func (a FollowerTable) FromSchema(schemaName string) *FollowerTable { + return newFollowerTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new FollowerTable with assigned table prefix +func (a FollowerTable) WithPrefix(prefix string) *FollowerTable { + return newFollowerTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new FollowerTable with assigned table suffix +func (a FollowerTable) WithSuffix(suffix string) *FollowerTable { + return newFollowerTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newFollowerTable(schemaName, tableName, alias string) *FollowerTable { + return &FollowerTable{ + followerTable: newFollowerTableImpl(schemaName, tableName, alias), + EXCLUDED: newFollowerTableImpl("", "excluded", ""), + } +} + +func newFollowerTableImpl(schemaName, tableName, alias string) followerTable { + var ( + IDColumn = sqlite.IntegerColumn("id") + AvatarColumn = sqlite.StringColumn("avatar") + NicknameColumn = sqlite.StringColumn("nickname") + SecUIDColumn = sqlite.StringColumn("sec_uid") + ShortIDColumn = sqlite.StringColumn("short_id") + UIDColumn = sqlite.StringColumn("uid") + UniqueIDColumn = sqlite.StringColumn("unique_id") + ExpertUIDColumn = sqlite.StringColumn("expert_uid") + FollowedColumn = sqlite.IntegerColumn("followed") + CreatedAtColumn = sqlite.IntegerColumn("created_at") + allColumns = sqlite.ColumnList{IDColumn, AvatarColumn, NicknameColumn, SecUIDColumn, ShortIDColumn, UIDColumn, UniqueIDColumn, ExpertUIDColumn, FollowedColumn, CreatedAtColumn} + mutableColumns = sqlite.ColumnList{AvatarColumn, NicknameColumn, SecUIDColumn, ShortIDColumn, UIDColumn, UniqueIDColumn, ExpertUIDColumn, FollowedColumn, CreatedAtColumn} + ) + + return followerTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + ID: IDColumn, + Avatar: AvatarColumn, + Nickname: NicknameColumn, + SecUID: SecUIDColumn, + ShortID: ShortIDColumn, + UID: UIDColumn, + UniqueID: UniqueIDColumn, + ExpertUID: ExpertUIDColumn, + Followed: FollowedColumn, + CreatedAt: CreatedAtColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/.gen/table/table_use_schema.go b/.gen/table/table_use_schema.go new file mode 100644 index 0000000..f8347ee --- /dev/null +++ b/.gen/table/table_use_schema.go @@ -0,0 +1,16 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +// UseSchema sets a new schema name for all generated table SQL builder types. It is recommended to invoke +// this method only once at the beginning of the program. +func UseSchema(schema string) { + Device = Device.FromSchema(schema) + Expert = Expert.FromSchema(schema) + Follower = Follower.FromSchema(schema) +} diff --git a/.gitignore b/.gitignore index 60d7971..146e34c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,3 @@ -# ---> AL -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -# Local History for Visual Studio Code -.history/ -*.app -.snapshots/* - +data.db +node_modules +__debug_* \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..acb80c1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "web", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/web/web.go", + "workspaceFolder": "/projects/douyin-proxy", + "args": [ + "--config", "/projects/douyin-proxy/config.yml" + ] + } + ] +} \ No newline at end of file diff --git a/Icon.png b/Icon.png new file mode 100644 index 0000000..b9c666c Binary files /dev/null and b/Icon.png differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8aab40d --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +model: + jet -source=sqlite -dsn="./data.db" -schema=follower -path=./.gen + +fmt: + gofumpt -w -l -extra . + +db: + rm -rf data.db; sqlite3 data.db < init.sql +sync: + scp -r dst root@10.1.1.105:/opt/netboot/www/ + +proxy: + rm -rf ./dst/* + CGO_ENABLE=1 go build -o dst/proxy ./cmd/proxy + cp -ap modules/web/dst dst/ + cp certs/ca.crt dst/ + rm -rf ./dst/data.db; sqlite3 ./dst/data.db < init.sql + + # rm -rf proxy.gz + # tar zcvf proxy.gz ./dst + # scp proxy.gz root@10.1.1.105:/opt/netboot/www/ +web: + CGO_ENABLE=1 go build -o ./dst/web ./cmd/web + +all: + rm -rf ./dst/* + cd frontend && npm run build && cd .. + CGO_ENABLE=0 go build -o ./dst/web ./cmd/web + CGO_ENABLE=0 go build -o ./dst/proxy ./cmd/proxy + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o dst/proxy.exe ./cmd/proxy + + echo 'start /d "D:\proxy" proxy.exe' > ./dst/proxy.bat + + win: + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o proxy.exe ./cmd/proxy + +publish: all + # scp -r dst/ root@39.105.111.158:/projects/douyin-proxy/ + rsync -aH --progress dst/ root@39.105.111.158:/projects/douyin-proxy/ \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index b160600..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# douyin-autojs-follower - diff --git a/certs/ca.crt b/certs/ca.crt new file mode 100644 index 0000000..62653da --- /dev/null +++ b/certs/ca.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD +VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM +B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0 +aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0 +MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE +CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV +BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI +hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9 +3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP +sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9 +V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh +hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr +lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq +j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo +WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD +fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj +YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh +WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj +UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4 +uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F +AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0 +C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3 +Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin +o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye +i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr +bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY +VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft +8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86 +NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV +BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA== +-----END CERTIFICATE----- diff --git a/certs/ca.pem b/certs/ca.pem new file mode 100644 index 0000000..62653da --- /dev/null +++ b/certs/ca.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD +VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM +B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0 +aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0 +MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE +CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV +BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI +hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9 +3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP +sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9 +V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh +hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr +lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq +j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo +WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD +fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj +YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh +WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj +UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4 +uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F +AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0 +C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3 +Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin +o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye +i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr +bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY +VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft +8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86 +NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV +BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA== +-----END CERTIFICATE----- diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 0000000..ea8c7e6 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,37 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cmd + +import ( + "os" + + "dyproxy/providers/db" + + "github.com/spf13/cobra" +) + +// initCmd represents the init command +var initCmd = &cobra.Command{ + Use: "init", + Short: "init db", + RunE: func(cmd *cobra.Command, args []string) error { + db, err := db.Connect("") + if err != nil { + return err + } + defer db.Close() + + initSql, _ := os.ReadFile("init.sql") + _, err = db.Exec(string(initSql)) + if err != nil { + return err + } + + return nil + }, +} + +func init() { + rootCmd.AddCommand(initCmd) +} diff --git a/cmd/proxy/apps.png b/cmd/proxy/apps.png new file mode 100644 index 0000000..3e5af99 Binary files /dev/null and b/cmd/proxy/apps.png differ diff --git a/cmd/proxy/ca.crt b/cmd/proxy/ca.crt new file mode 100644 index 0000000..62653da --- /dev/null +++ b/cmd/proxy/ca.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD +VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM +B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0 +aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0 +MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE +CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV +BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI +hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9 +3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP +sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9 +V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh +hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr +lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq +j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo +WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD +fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj +YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh +WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj +UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4 +uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F +AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0 +C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3 +Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin +o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye +i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr +bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY +VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft +8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86 +NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV +BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA== +-----END CERTIFICATE----- diff --git a/cmd/proxy/icon.ico b/cmd/proxy/icon.ico new file mode 100644 index 0000000..6f0c60a Binary files /dev/null and b/cmd/proxy/icon.ico differ diff --git a/cmd/proxy/proxy.manifest b/cmd/proxy/proxy.manifest new file mode 100644 index 0000000..1492a19 --- /dev/null +++ b/cmd/proxy/proxy.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + PerMonitorV2, PerMonitor + True + + + \ No newline at end of file diff --git a/cmd/proxy/proxy_linux.go b/cmd/proxy/proxy_linux.go new file mode 100644 index 0000000..70619d1 --- /dev/null +++ b/cmd/proxy/proxy_linux.go @@ -0,0 +1,24 @@ +package main + +import ( + "log" + + "dyproxy/modules/proxy" + + "github.com/spf13/cobra" +) + +func main() { + cmd := &cobra.Command{ + Use: "proxy", + Short: "serve proxy", + RunE: proxy.ServeE, + } + cmd.Flags().StringP("host", "H", "https://f.jdwan.com", "default post data to host") + cmd.Flags().BoolP("debug", "D", false, "debug mode") + cmd.Flags().IntP("duration", "d", 10, "fans fetch duration") + + if err := cmd.Execute(); err != nil { + log.Fatal(err) + } +} diff --git a/cmd/proxy/proxy_windows.go b/cmd/proxy/proxy_windows.go new file mode 100644 index 0000000..428d601 --- /dev/null +++ b/cmd/proxy/proxy_windows.go @@ -0,0 +1,114 @@ +package main + +import ( + _ "embed" + "fmt" + "os" + "os/exec" + "strings" + "time" + + "dyproxy/modules/proxy" + + "github.com/lxn/walk" + . "github.com/lxn/walk/declarative" + "github.com/sirupsen/logrus" +) + +var ( + outTE *walk.TextEdit + wnd *walk.MainWindow + btnService *walk.PushButton + err error + pr *proxy.Proxy +) + +//go:embed ca.crt +var ca []byte + +func init() { + // if go os is windows + if strings.ToLower(os.Getenv("GOOS")) == "windows" { + // get tmp path + tmpPath := os.TempDir() + // write ca.crt to tmp path + err := os.WriteFile(tmpPath+"/ca.crt", ca, 0o644) + if err != nil { + logrus.Fatal(err) + } + logrus.Info("import root cert") + // run certutil.exe -addstore root tmpPath+"/ca.crt" + cmd := exec.Command("certutil.exe", "-addstore", "root", tmpPath+"/ca.crt") + err = cmd.Run() + if err != nil { + logrus.Fatal(err) + } + } +} + +func main() { + MainWindow{ + AssignTo: &wnd, + Title: "粉丝代理服务", + Size: Size{300, 300}, + Layout: VBox{}, + Children: []Widget{ + PushButton{ + Text: "开启服务", + MinSize: Size{300, 50}, + AssignTo: &btnService, + OnClicked: func() { + errChan := make(chan error) + if btnService.Text() == "开启服务" { + btnService.SetEnabled(false) + outTE.AppendText("服务启动中...\r\n") + if pr == nil { + pr = proxy.NewProxy("https://f.jdwan.com", false, 10) + + go func() { + errChan <- pr.Serve(29999) + }() + } + select { + case err := <-errChan: + outTE.AppendText(err.Error() + "\r\n") + case <-time.After(2 * time.Second): + outTE.AppendText("服务启动成功\r\n") + btnService.SetText("停止服务") + go func() { + err = <-errChan + outTE.AppendText("[ERR] " + err.Error() + "\r\n") + btnService.SetText("开启服务") + btnService.SetEnabled(true) + }() + } + btnService.SetEnabled(true) + } else { + btnService.SetEnabled(false) + outTE.AppendText("服务停止中...\r\n") + pr.Shutdown() + outTE.AppendText("服务停止成功\r\n") + btnService.SetText("开启服务") + btnService.SetEnabled(true) + } + }, + }, + TextEdit{AssignTo: &outTE, ReadOnly: true}, + }, + }.Create() + + wnd.Closing().Attach(func(canceled *bool, reason walk.CloseReason) { + fmt.Println("Application closing.") + if pr != nil { + outTE.AppendText("服务停止中...\r\n") + pr.Shutdown() + outTE.AppendText("服务停止成功\r\n") + } + }) + + wnd.Disposing().Attach(func() { + fmt.Println("Application has exited.") + }) + + wnd.Run() +} diff --git a/cmd/proxy/rsrc.syso b/cmd/proxy/rsrc.syso new file mode 100644 index 0000000..bc42df7 Binary files /dev/null and b/cmd/proxy/rsrc.syso differ diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..149eb84 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,46 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "dyproxy", + Short: "A brief description of your application", + Long: `A longer description that spans multiple lines and likely contains +examples and usage of using your application. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dyproxy.yaml)") + + // Cobra also supports local flags, which will only run + // when this action is called directly. + rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/test.go b/cmd/test.go new file mode 100644 index 0000000..4e72e47 --- /dev/null +++ b/cmd/test.go @@ -0,0 +1,88 @@ +/* +Copyright © 2024 NAME HERE +*/ +package cmd + +import ( + "errors" + "log" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "dyproxy/.gen/model" + "dyproxy/.gen/table" + "dyproxy/providers/db" + + _ "github.com/mattn/go-sqlite3" + + "github.com/go-jet/jet/v2/qrm" + . "github.com/go-jet/jet/v2/sqlite" +) + +// testCmd represents the test command +var testCmd = &cobra.Command{ + Use: "test", + Short: "A brief description of your command", + Run: func(cmd *cobra.Command, argvs []string) { + db, err := db.Connect("") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + expert := model.Expert{ + UID: "123", + SecUID: "123", + ShortID: "123", + RealName: "123", + NickName: "123", + } + + logrus.Warnf("expert: %+v", expert) + stmt := table.Expert.SELECT(table.Expert.AllColumns).WHERE(table.Expert.UID.EQ(String(expert.UID))) + sql, args := stmt.Sql() + logrus.Debugf("sql: %s args: %+v", sql, args) + + var ui model.Expert + if err := stmt.Query(db, &ui); err != nil { + if errors.Is(err, qrm.ErrNoRows) { + _, err = table.Expert.INSERT( + table.Expert.UID, + table.Expert.SecUID, + table.Expert.ShortID, + table.Expert.RealName, + table.Expert.NickName, + ).VALUES( + String(expert.UID), + String(expert.SecUID), + String(expert.ShortID), + String(expert.RealName), + String(expert.NickName), + ).Exec(db) + if err != nil { + log.Fatal(err) + } + + log.Println("no rows") + return + } + log.Fatal("123", err) + } + log.Println("ID", ui) + }, +} + +func init() { + rootCmd.AddCommand(testCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // testCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // testCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/web/web.go b/cmd/web/web.go new file mode 100644 index 0000000..db79b84 --- /dev/null +++ b/cmd/web/web.go @@ -0,0 +1,23 @@ +package main + +import ( + "log" + + "dyproxy/modules/web" + + "github.com/spf13/cobra" +) + +// webCmd represents the web command +func main() { + cmd := &cobra.Command{ + Use: "web", + Short: "A brief description of your command", + RunE: web.ServeE, + } + + cmd.Flags().String("config", "config.yml", "config file") + if err := cmd.Execute(); err != nil { + log.Fatal(err) + } +} diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..ec2ab25 --- /dev/null +++ b/config.yml @@ -0,0 +1,3 @@ +version: 1.0.1 +path: /projects/douyin-robot +static: /projects/douyin-proxy-static diff --git a/contracts/runner.go b/contracts/runner.go new file mode 100644 index 0000000..6cdb93a --- /dev/null +++ b/contracts/runner.go @@ -0,0 +1 @@ +package contracts diff --git a/dst/.gitignore b/dst/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/dst/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/.vscode/extensions.json b/frontend/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/frontend/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..0bfecb0 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,9 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..e90ba43 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,27 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --host", + "build": "vue-tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "axios": "^1.7.2", + "moment": "^2.30.1", + "vue": "^3.4.21" + }, + "devDependencies": { + "@tailwindcss/typography": "^0.5.13", + "@vitejs/plugin-vue": "^5.0.4", + "autoprefixer": "^10.4.19", + "daisyui": "^4.11.1", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5.2.2", + "vite": "^5.2.0", + "vue-tsc": "^2.0.6" + } +} diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml new file mode 100644 index 0000000..4c836b4 --- /dev/null +++ b/frontend/pnpm-lock.yaml @@ -0,0 +1,1807 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + axios: + specifier: ^1.7.2 + version: 1.7.2 + moment: + specifier: ^2.30.1 + version: 2.30.1 + vue: + specifier: ^3.4.21 + version: 3.4.27(typescript@5.4.5) + devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.13 + version: 0.5.13(tailwindcss@3.4.3) + '@vitejs/plugin-vue': + specifier: ^5.0.4 + version: 5.0.4(vite@5.2.11)(vue@3.4.27(typescript@5.4.5)) + autoprefixer: + specifier: ^10.4.19 + version: 10.4.19(postcss@8.4.38) + daisyui: + specifier: ^4.11.1 + version: 4.11.1(postcss@8.4.38) + postcss: + specifier: ^8.4.38 + version: 8.4.38 + tailwindcss: + specifier: ^3.4.3 + version: 3.4.3 + typescript: + specifier: ^5.2.2 + version: 5.4.5 + vite: + specifier: ^5.2.0 + version: 5.2.11 + vue-tsc: + specifier: ^2.0.6 + version: 2.0.19(typescript@5.4.5) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, tarball: https://npm.hub.118848.xyz/repository/npm/@alloc/quick-lru/-/quick-lru-5.2.0.tgz} + engines: {node: '>=10'} + + '@babel/helper-string-parser@7.24.1': + resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.5': + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==, tarball: https://npm.hub.118848.xyz/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.5': + resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==, tarball: https://npm.hub.118848.xyz/repository/npm/@babel/parser/-/parser-7.24.5.tgz} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.24.5': + resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@babel/types/-/types-7.24.5.tgz} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.20.2': + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.20.2': + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.20.2': + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/android-arm/-/android-arm-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.20.2': + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/android-x64/-/android-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.20.2': + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.20.2': + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.20.2': + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.20.2': + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.20.2': + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.20.2': + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.20.2': + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.20.2': + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.20.2': + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.20.2': + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.20.2': + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.20.2': + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.20.2': + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.20.2': + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.20.2': + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.20.2': + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.20.2': + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.20.2': + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.20.2': + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==, tarball: https://npm.hub.118848.xyz/repository/npm/@isaacs/cliui/-/cliui-8.0.2.tgz} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==, tarball: https://npm.hub.118848.xyz/repository/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, tarball: https://npm.hub.118848.xyz/repository/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==, tarball: https://npm.hub.118848.xyz/repository/npm/@jridgewell/set-array/-/set-array-1.2.1.tgz} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==, tarball: https://npm.hub.118848.xyz/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, tarball: https://npm.hub.118848.xyz/repository/npm/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, tarball: https://npm.hub.118848.xyz/repository/npm/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, tarball: https://npm.hub.118848.xyz/repository/npm/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, tarball: https://npm.hub.118848.xyz/repository/npm/@pkgjs/parseargs/-/parseargs-0.11.0.tgz} + engines: {node: '>=14'} + + '@rollup/rollup-android-arm-eabi@4.18.0': + resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.0': + resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.0': + resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.0': + resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.18.0': + resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.18.0': + resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.18.0': + resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.18.0': + resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-s390x-gnu@4.18.0': + resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.18.0': + resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.18.0': + resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-win32-arm64-msvc@4.18.0': + resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.0': + resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.0': + resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==, tarball: https://npm.hub.118848.xyz/repository/npm/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz} + cpu: [x64] + os: [win32] + + '@tailwindcss/typography@0.5.13': + resolution: {integrity: sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==, tarball: https://npm.hub.118848.xyz/repository/npm/@tailwindcss/typography/-/typography-0.5.13.tgz} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==, tarball: https://npm.hub.118848.xyz/repository/npm/@types/estree/-/estree-1.0.5.tgz} + + '@vitejs/plugin-vue@5.0.4': + resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.2.4': + resolution: {integrity: sha512-7As47GndxGxsqqYnbreLrfB5NDUeQioPM2LJKUuB4/34c0NpEJ2byVl3c9KYdjIdiEstWZ9JLtLKNTaPWb5jtA==, tarball: https://npm.hub.118848.xyz/repository/npm/@volar/language-core/-/language-core-2.2.4.tgz} + + '@volar/source-map@2.2.4': + resolution: {integrity: sha512-m92FLpR9vB1YEZfiZ+bfgpLrToL/DNkOrorWVep3pffHrwwI4Tx2oIQN+sqHJfKkiT5N3J1owC+8crhAEinfjg==, tarball: https://npm.hub.118848.xyz/repository/npm/@volar/source-map/-/source-map-2.2.4.tgz} + + '@volar/typescript@2.2.4': + resolution: {integrity: sha512-uAQC53tgEbHO62G8NXMfmBrJAlP2QJ9WxVEEQqqK3I6VSy8frL5LbH3hAWODxiwMWixv74wJLWlKbWXOgdIoRQ==, tarball: https://npm.hub.118848.xyz/repository/npm/@volar/typescript/-/typescript-2.2.4.tgz} + + '@vue/compiler-core@3.4.27': + resolution: {integrity: sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/compiler-core/-/compiler-core-3.4.27.tgz} + + '@vue/compiler-dom@3.4.27': + resolution: {integrity: sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz} + + '@vue/compiler-sfc@3.4.27': + resolution: {integrity: sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz} + + '@vue/compiler-ssr@3.4.27': + resolution: {integrity: sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz} + + '@vue/language-core@2.0.19': + resolution: {integrity: sha512-A9EGOnvb51jOvnCYoRLnMP+CcoPlbZVxI9gZXE/y2GksRWM6j/PrLEIC++pnosWTN08tFpJgxhSS//E9v/Sg+Q==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/language-core/-/language-core-2.0.19.tgz} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.4.27': + resolution: {integrity: sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/reactivity/-/reactivity-3.4.27.tgz} + + '@vue/runtime-core@3.4.27': + resolution: {integrity: sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/runtime-core/-/runtime-core-3.4.27.tgz} + + '@vue/runtime-dom@3.4.27': + resolution: {integrity: sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz} + + '@vue/server-renderer@3.4.27': + resolution: {integrity: sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/server-renderer/-/server-renderer-3.4.27.tgz} + peerDependencies: + vue: 3.4.27 + + '@vue/shared@3.4.27': + resolution: {integrity: sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==, tarball: https://npm.hub.118848.xyz/repository/npm/@vue/shared/-/shared-3.4.27.tgz} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, tarball: https://npm.hub.118848.xyz/repository/npm/ansi-regex/-/ansi-regex-5.0.1.tgz} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==, tarball: https://npm.hub.118848.xyz/repository/npm/ansi-regex/-/ansi-regex-6.0.1.tgz} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, tarball: https://npm.hub.118848.xyz/repository/npm/ansi-styles/-/ansi-styles-4.3.0.tgz} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==, tarball: https://npm.hub.118848.xyz/repository/npm/ansi-styles/-/ansi-styles-6.2.1.tgz} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, tarball: https://npm.hub.118848.xyz/repository/npm/any-promise/-/any-promise-1.3.0.tgz} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, tarball: https://npm.hub.118848.xyz/repository/npm/anymatch/-/anymatch-3.1.3.tgz} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==, tarball: https://npm.hub.118848.xyz/repository/npm/arg/-/arg-5.0.2.tgz} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, tarball: https://npm.hub.118848.xyz/repository/npm/asynckit/-/asynckit-0.4.0.tgz} + + autoprefixer@10.4.19: + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==, tarball: https://npm.hub.118848.xyz/repository/npm/autoprefixer/-/autoprefixer-10.4.19.tgz} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==, tarball: https://npm.hub.118848.xyz/repository/npm/axios/-/axios-1.7.2.tgz} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, tarball: https://npm.hub.118848.xyz/repository/npm/balanced-match/-/balanced-match-1.0.2.tgz} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, tarball: https://npm.hub.118848.xyz/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz} + engines: {node: '>=8'} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==, tarball: https://npm.hub.118848.xyz/repository/npm/brace-expansion/-/brace-expansion-2.0.1.tgz} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, tarball: https://npm.hub.118848.xyz/repository/npm/braces/-/braces-3.0.3.tgz} + engines: {node: '>=8'} + + browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==, tarball: https://npm.hub.118848.xyz/repository/npm/browserslist/-/browserslist-4.23.0.tgz} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==, tarball: https://npm.hub.118848.xyz/repository/npm/camelcase-css/-/camelcase-css-2.0.1.tgz} + engines: {node: '>= 6'} + + caniuse-lite@1.0.30001621: + resolution: {integrity: sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==, tarball: https://npm.hub.118848.xyz/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==, tarball: https://npm.hub.118848.xyz/repository/npm/chokidar/-/chokidar-3.6.0.tgz} + engines: {node: '>= 8.10.0'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, tarball: https://npm.hub.118848.xyz/repository/npm/color-convert/-/color-convert-2.0.1.tgz} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, tarball: https://npm.hub.118848.xyz/repository/npm/color-name/-/color-name-1.1.4.tgz} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, tarball: https://npm.hub.118848.xyz/repository/npm/combined-stream/-/combined-stream-1.0.8.tgz} + engines: {node: '>= 0.8'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, tarball: https://npm.hub.118848.xyz/repository/npm/commander/-/commander-4.1.1.tgz} + engines: {node: '>= 6'} + + computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==, tarball: https://npm.hub.118848.xyz/repository/npm/computeds/-/computeds-0.0.1.tgz} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==, tarball: https://npm.hub.118848.xyz/repository/npm/cross-spawn/-/cross-spawn-7.0.3.tgz} + engines: {node: '>= 8'} + + css-selector-tokenizer@0.8.0: + resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==, tarball: https://npm.hub.118848.xyz/repository/npm/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, tarball: https://npm.hub.118848.xyz/repository/npm/cssesc/-/cssesc-3.0.0.tgz} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==, tarball: https://npm.hub.118848.xyz/repository/npm/csstype/-/csstype-3.1.3.tgz} + + culori@3.3.0: + resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==, tarball: https://npm.hub.118848.xyz/repository/npm/culori/-/culori-3.3.0.tgz} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + daisyui@4.11.1: + resolution: {integrity: sha512-obT9CUbQdW6eoHwSeT5VwaRrWlwrM4OT5qlfdJ0oQlSIEYhwnEl2+L2fwu5PioLbitwuMdYC2X8I1cyy8Pf6LQ==, tarball: https://npm.hub.118848.xyz/repository/npm/daisyui/-/daisyui-4.11.1.tgz} + engines: {node: '>=16.9.0'} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==, tarball: https://npm.hub.118848.xyz/repository/npm/de-indent/-/de-indent-1.0.2.tgz} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, tarball: https://npm.hub.118848.xyz/repository/npm/delayed-stream/-/delayed-stream-1.0.0.tgz} + engines: {node: '>=0.4.0'} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==, tarball: https://npm.hub.118848.xyz/repository/npm/didyoumean/-/didyoumean-1.2.2.tgz} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==, tarball: https://npm.hub.118848.xyz/repository/npm/dlv/-/dlv-1.1.3.tgz} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, tarball: https://npm.hub.118848.xyz/repository/npm/eastasianwidth/-/eastasianwidth-0.2.0.tgz} + + electron-to-chromium@1.4.778: + resolution: {integrity: sha512-C6q/xcUJf/2yODRxAVCfIk4j3y3LMsD0ehiE2RQNV2cxc8XU62gR6vvYh3+etSUzlgTfil+qDHI1vubpdf0TOA==, tarball: https://npm.hub.118848.xyz/repository/npm/electron-to-chromium/-/electron-to-chromium-1.4.778.tgz} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, tarball: https://npm.hub.118848.xyz/repository/npm/emoji-regex/-/emoji-regex-8.0.0.tgz} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, tarball: https://npm.hub.118848.xyz/repository/npm/emoji-regex/-/emoji-regex-9.2.2.tgz} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, tarball: https://npm.hub.118848.xyz/repository/npm/entities/-/entities-4.5.0.tgz} + engines: {node: '>=0.12'} + + esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==, tarball: https://npm.hub.118848.xyz/repository/npm/esbuild/-/esbuild-0.20.2.tgz} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==, tarball: https://npm.hub.118848.xyz/repository/npm/escalade/-/escalade-3.1.2.tgz} + engines: {node: '>=6'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, tarball: https://npm.hub.118848.xyz/repository/npm/estree-walker/-/estree-walker-2.0.2.tgz} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==, tarball: https://npm.hub.118848.xyz/repository/npm/fast-glob/-/fast-glob-3.3.2.tgz} + engines: {node: '>=8.6.0'} + + fastparse@1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==, tarball: https://npm.hub.118848.xyz/repository/npm/fastparse/-/fastparse-1.1.2.tgz} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==, tarball: https://npm.hub.118848.xyz/repository/npm/fastq/-/fastq-1.17.1.tgz} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, tarball: https://npm.hub.118848.xyz/repository/npm/fill-range/-/fill-range-7.1.1.tgz} + engines: {node: '>=8'} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==, tarball: https://npm.hub.118848.xyz/repository/npm/follow-redirects/-/follow-redirects-1.15.6.tgz} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==, tarball: https://npm.hub.118848.xyz/repository/npm/foreground-child/-/foreground-child-3.1.1.tgz} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==, tarball: https://npm.hub.118848.xyz/repository/npm/form-data/-/form-data-4.0.0.tgz} + engines: {node: '>= 6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==, tarball: https://npm.hub.118848.xyz/repository/npm/fraction.js/-/fraction.js-4.3.7.tgz} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://npm.hub.118848.xyz/repository/npm/fsevents/-/fsevents-2.3.3.tgz} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, tarball: https://npm.hub.118848.xyz/repository/npm/function-bind/-/function-bind-1.1.2.tgz} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, tarball: https://npm.hub.118848.xyz/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, tarball: https://npm.hub.118848.xyz/repository/npm/glob-parent/-/glob-parent-6.0.2.tgz} + engines: {node: '>=10.13.0'} + + glob@10.3.16: + resolution: {integrity: sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==, tarball: https://npm.hub.118848.xyz/repository/npm/glob/-/glob-10.3.16.tgz} + engines: {node: '>=16 || 14 >=14.18'} + hasBin: true + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, tarball: https://npm.hub.118848.xyz/repository/npm/hasown/-/hasown-2.0.2.tgz} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, tarball: https://npm.hub.118848.xyz/repository/npm/he/-/he-1.2.0.tgz} + hasBin: true + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, tarball: https://npm.hub.118848.xyz/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz} + engines: {node: '>=8'} + + is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==, tarball: https://npm.hub.118848.xyz/repository/npm/is-core-module/-/is-core-module-2.13.1.tgz} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, tarball: https://npm.hub.118848.xyz/repository/npm/is-extglob/-/is-extglob-2.1.1.tgz} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, tarball: https://npm.hub.118848.xyz/repository/npm/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, tarball: https://npm.hub.118848.xyz/repository/npm/is-glob/-/is-glob-4.0.3.tgz} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, tarball: https://npm.hub.118848.xyz/repository/npm/is-number/-/is-number-7.0.0.tgz} + engines: {node: '>=0.12.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, tarball: https://npm.hub.118848.xyz/repository/npm/isexe/-/isexe-2.0.0.tgz} + + jackspeak@3.1.2: + resolution: {integrity: sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==, tarball: https://npm.hub.118848.xyz/repository/npm/jackspeak/-/jackspeak-3.1.2.tgz} + engines: {node: '>=14'} + + jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==, tarball: https://npm.hub.118848.xyz/repository/npm/jiti/-/jiti-1.21.0.tgz} + hasBin: true + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==, tarball: https://npm.hub.118848.xyz/repository/npm/lilconfig/-/lilconfig-2.1.0.tgz} + engines: {node: '>=10'} + + lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==, tarball: https://npm.hub.118848.xyz/repository/npm/lilconfig/-/lilconfig-3.1.1.tgz} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, tarball: https://npm.hub.118848.xyz/repository/npm/lines-and-columns/-/lines-and-columns-1.2.4.tgz} + + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==, tarball: https://npm.hub.118848.xyz/repository/npm/lodash.castarray/-/lodash.castarray-4.4.0.tgz} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==, tarball: https://npm.hub.118848.xyz/repository/npm/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, tarball: https://npm.hub.118848.xyz/repository/npm/lodash.merge/-/lodash.merge-4.6.2.tgz} + + lru-cache@10.2.2: + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==, tarball: https://npm.hub.118848.xyz/repository/npm/lru-cache/-/lru-cache-10.2.2.tgz} + engines: {node: 14 || >=16.14} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==, tarball: https://npm.hub.118848.xyz/repository/npm/magic-string/-/magic-string-0.30.10.tgz} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, tarball: https://npm.hub.118848.xyz/repository/npm/merge2/-/merge2-1.4.1.tgz} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==, tarball: https://npm.hub.118848.xyz/repository/npm/micromatch/-/micromatch-4.0.7.tgz} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, tarball: https://npm.hub.118848.xyz/repository/npm/mime-db/-/mime-db-1.52.0.tgz} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, tarball: https://npm.hub.118848.xyz/repository/npm/mime-types/-/mime-types-2.1.35.tgz} + engines: {node: '>= 0.6'} + + minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==, tarball: https://npm.hub.118848.xyz/repository/npm/minimatch/-/minimatch-9.0.4.tgz} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.1: + resolution: {integrity: sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==, tarball: https://npm.hub.118848.xyz/repository/npm/minipass/-/minipass-7.1.1.tgz} + engines: {node: '>=16 || 14 >=14.17'} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==, tarball: https://npm.hub.118848.xyz/repository/npm/moment/-/moment-2.30.1.tgz} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==, tarball: https://npm.hub.118848.xyz/repository/npm/muggle-string/-/muggle-string-0.4.1.tgz} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, tarball: https://npm.hub.118848.xyz/repository/npm/mz/-/mz-2.7.0.tgz} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==, tarball: https://npm.hub.118848.xyz/repository/npm/nanoid/-/nanoid-3.3.7.tgz} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==, tarball: https://npm.hub.118848.xyz/repository/npm/node-releases/-/node-releases-2.0.14.tgz} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, tarball: https://npm.hub.118848.xyz/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==, tarball: https://npm.hub.118848.xyz/repository/npm/normalize-range/-/normalize-range-0.1.2.tgz} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, tarball: https://npm.hub.118848.xyz/repository/npm/object-assign/-/object-assign-4.1.1.tgz} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==, tarball: https://npm.hub.118848.xyz/repository/npm/object-hash/-/object-hash-3.0.0.tgz} + engines: {node: '>= 6'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, tarball: https://npm.hub.118848.xyz/repository/npm/path-browserify/-/path-browserify-1.0.1.tgz} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, tarball: https://npm.hub.118848.xyz/repository/npm/path-key/-/path-key-3.1.1.tgz} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, tarball: https://npm.hub.118848.xyz/repository/npm/path-parse/-/path-parse-1.0.7.tgz} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==, tarball: https://npm.hub.118848.xyz/repository/npm/path-scurry/-/path-scurry-1.11.1.tgz} + engines: {node: '>=16 || 14 >=14.18'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==, tarball: https://npm.hub.118848.xyz/repository/npm/picocolors/-/picocolors-1.0.1.tgz} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, tarball: https://npm.hub.118848.xyz/repository/npm/picomatch/-/picomatch-2.3.1.tgz} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, tarball: https://npm.hub.118848.xyz/repository/npm/pify/-/pify-2.3.0.tgz} + engines: {node: '>=0.10.0'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==, tarball: https://npm.hub.118848.xyz/repository/npm/pirates/-/pirates-4.0.6.tgz} + engines: {node: '>= 6'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss-import/-/postcss-import-15.1.0.tgz} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss-js/-/postcss-js-4.0.1.tgz} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss-load-config/-/postcss-load-config-4.0.2.tgz} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.0.1: + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss-nested/-/postcss-nested-6.0.1.tgz} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz} + engines: {node: '>=4'} + + postcss-selector-parser@6.1.0: + resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz} + + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==, tarball: https://npm.hub.118848.xyz/repository/npm/postcss/-/postcss-8.4.38.tgz} + engines: {node: ^10 || ^12 || >=14} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, tarball: https://npm.hub.118848.xyz/repository/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, tarball: https://npm.hub.118848.xyz/repository/npm/queue-microtask/-/queue-microtask-1.2.3.tgz} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==, tarball: https://npm.hub.118848.xyz/repository/npm/read-cache/-/read-cache-1.0.0.tgz} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, tarball: https://npm.hub.118848.xyz/repository/npm/readdirp/-/readdirp-3.6.0.tgz} + engines: {node: '>=8.10.0'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==, tarball: https://npm.hub.118848.xyz/repository/npm/resolve/-/resolve-1.22.8.tgz} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==, tarball: https://npm.hub.118848.xyz/repository/npm/reusify/-/reusify-1.0.4.tgz} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.18.0: + resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==, tarball: https://npm.hub.118848.xyz/repository/npm/rollup/-/rollup-4.18.0.tgz} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, tarball: https://npm.hub.118848.xyz/repository/npm/run-parallel/-/run-parallel-1.2.0.tgz} + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==, tarball: https://npm.hub.118848.xyz/repository/npm/semver/-/semver-7.6.2.tgz} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, tarball: https://npm.hub.118848.xyz/repository/npm/shebang-command/-/shebang-command-2.0.0.tgz} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, tarball: https://npm.hub.118848.xyz/repository/npm/shebang-regex/-/shebang-regex-3.0.0.tgz} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, tarball: https://npm.hub.118848.xyz/repository/npm/signal-exit/-/signal-exit-4.1.0.tgz} + engines: {node: '>=14'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==, tarball: https://npm.hub.118848.xyz/repository/npm/source-map-js/-/source-map-js-1.2.0.tgz} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, tarball: https://npm.hub.118848.xyz/repository/npm/string-width/-/string-width-4.2.3.tgz} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==, tarball: https://npm.hub.118848.xyz/repository/npm/string-width/-/string-width-5.1.2.tgz} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, tarball: https://npm.hub.118848.xyz/repository/npm/strip-ansi/-/strip-ansi-6.0.1.tgz} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==, tarball: https://npm.hub.118848.xyz/repository/npm/strip-ansi/-/strip-ansi-7.1.0.tgz} + engines: {node: '>=12'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==, tarball: https://npm.hub.118848.xyz/repository/npm/sucrase/-/sucrase-3.35.0.tgz} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, tarball: https://npm.hub.118848.xyz/repository/npm/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz} + engines: {node: '>= 0.4'} + + tailwindcss@3.4.3: + resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==, tarball: https://npm.hub.118848.xyz/repository/npm/tailwindcss/-/tailwindcss-3.4.3.tgz} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==, tarball: https://npm.hub.118848.xyz/repository/npm/thenify-all/-/thenify-all-1.6.0.tgz} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, tarball: https://npm.hub.118848.xyz/repository/npm/thenify/-/thenify-3.3.1.tgz} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==, tarball: https://npm.hub.118848.xyz/repository/npm/to-fast-properties/-/to-fast-properties-2.0.0.tgz} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, tarball: https://npm.hub.118848.xyz/repository/npm/to-regex-range/-/to-regex-range-5.0.1.tgz} + engines: {node: '>=8.0'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==, tarball: https://npm.hub.118848.xyz/repository/npm/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz} + + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==, tarball: https://npm.hub.118848.xyz/repository/npm/typescript/-/typescript-5.4.5.tgz} + engines: {node: '>=14.17'} + hasBin: true + + update-browserslist-db@1.0.16: + resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==, tarball: https://npm.hub.118848.xyz/repository/npm/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, tarball: https://npm.hub.118848.xyz/repository/npm/util-deprecate/-/util-deprecate-1.0.2.tgz} + + vite@5.2.11: + resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==, tarball: https://npm.hub.118848.xyz/repository/npm/vite/-/vite-5.2.11.tgz} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==, tarball: https://npm.hub.118848.xyz/repository/npm/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz} + + vue-tsc@2.0.19: + resolution: {integrity: sha512-JWay5Zt2/871iodGF72cELIbcAoPyhJxq56mPPh+M2K7IwI688FMrFKc/+DvB05wDWEuCPexQJ6L10zSwzzapg==, tarball: https://npm.hub.118848.xyz/repository/npm/vue-tsc/-/vue-tsc-2.0.19.tgz} + hasBin: true + peerDependencies: + typescript: '*' + + vue@3.4.27: + resolution: {integrity: sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==, tarball: https://npm.hub.118848.xyz/repository/npm/vue/-/vue-3.4.27.tgz} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, tarball: https://npm.hub.118848.xyz/repository/npm/which/-/which-2.0.2.tgz} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, tarball: https://npm.hub.118848.xyz/repository/npm/wrap-ansi/-/wrap-ansi-7.0.0.tgz} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==, tarball: https://npm.hub.118848.xyz/repository/npm/wrap-ansi/-/wrap-ansi-8.1.0.tgz} + engines: {node: '>=12'} + + yaml@2.4.2: + resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==, tarball: https://npm.hub.118848.xyz/repository/npm/yaml/-/yaml-2.4.2.tgz} + engines: {node: '>= 14'} + hasBin: true + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@babel/helper-string-parser@7.24.1': {} + + '@babel/helper-validator-identifier@7.24.5': {} + + '@babel/parser@7.24.5': + dependencies: + '@babel/types': 7.24.5 + + '@babel/types@7.24.5': + dependencies: + '@babel/helper-string-parser': 7.24.1 + '@babel/helper-validator-identifier': 7.24.5 + to-fast-properties: 2.0.0 + + '@esbuild/aix-ppc64@0.20.2': + optional: true + + '@esbuild/android-arm64@0.20.2': + optional: true + + '@esbuild/android-arm@0.20.2': + optional: true + + '@esbuild/android-x64@0.20.2': + optional: true + + '@esbuild/darwin-arm64@0.20.2': + optional: true + + '@esbuild/darwin-x64@0.20.2': + optional: true + + '@esbuild/freebsd-arm64@0.20.2': + optional: true + + '@esbuild/freebsd-x64@0.20.2': + optional: true + + '@esbuild/linux-arm64@0.20.2': + optional: true + + '@esbuild/linux-arm@0.20.2': + optional: true + + '@esbuild/linux-ia32@0.20.2': + optional: true + + '@esbuild/linux-loong64@0.20.2': + optional: true + + '@esbuild/linux-mips64el@0.20.2': + optional: true + + '@esbuild/linux-ppc64@0.20.2': + optional: true + + '@esbuild/linux-riscv64@0.20.2': + optional: true + + '@esbuild/linux-s390x@0.20.2': + optional: true + + '@esbuild/linux-x64@0.20.2': + optional: true + + '@esbuild/netbsd-x64@0.20.2': + optional: true + + '@esbuild/openbsd-x64@0.20.2': + optional: true + + '@esbuild/sunos-x64@0.20.2': + optional: true + + '@esbuild/win32-arm64@0.20.2': + optional: true + + '@esbuild/win32-ia32@0.20.2': + optional: true + + '@esbuild/win32-x64@0.20.2': + optional: true + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.4.15': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.18.0': + optional: true + + '@rollup/rollup-android-arm64@4.18.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.18.0': + optional: true + + '@rollup/rollup-darwin-x64@4.18.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.18.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.18.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.18.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.18.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.18.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.18.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.18.0': + optional: true + + '@tailwindcss/typography@0.5.13(tailwindcss@3.4.3)': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.3 + + '@types/estree@1.0.5': {} + + '@vitejs/plugin-vue@5.0.4(vite@5.2.11)(vue@3.4.27(typescript@5.4.5))': + dependencies: + vite: 5.2.11 + vue: 3.4.27(typescript@5.4.5) + + '@volar/language-core@2.2.4': + dependencies: + '@volar/source-map': 2.2.4 + + '@volar/source-map@2.2.4': + dependencies: + muggle-string: 0.4.1 + + '@volar/typescript@2.2.4': + dependencies: + '@volar/language-core': 2.2.4 + path-browserify: 1.0.1 + + '@vue/compiler-core@3.4.27': + dependencies: + '@babel/parser': 7.24.5 + '@vue/shared': 3.4.27 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + + '@vue/compiler-dom@3.4.27': + dependencies: + '@vue/compiler-core': 3.4.27 + '@vue/shared': 3.4.27 + + '@vue/compiler-sfc@3.4.27': + dependencies: + '@babel/parser': 7.24.5 + '@vue/compiler-core': 3.4.27 + '@vue/compiler-dom': 3.4.27 + '@vue/compiler-ssr': 3.4.27 + '@vue/shared': 3.4.27 + estree-walker: 2.0.2 + magic-string: 0.30.10 + postcss: 8.4.38 + source-map-js: 1.2.0 + + '@vue/compiler-ssr@3.4.27': + dependencies: + '@vue/compiler-dom': 3.4.27 + '@vue/shared': 3.4.27 + + '@vue/language-core@2.0.19(typescript@5.4.5)': + dependencies: + '@volar/language-core': 2.2.4 + '@vue/compiler-dom': 3.4.27 + '@vue/shared': 3.4.27 + computeds: 0.0.1 + minimatch: 9.0.4 + path-browserify: 1.0.1 + vue-template-compiler: 2.7.16 + optionalDependencies: + typescript: 5.4.5 + + '@vue/reactivity@3.4.27': + dependencies: + '@vue/shared': 3.4.27 + + '@vue/runtime-core@3.4.27': + dependencies: + '@vue/reactivity': 3.4.27 + '@vue/shared': 3.4.27 + + '@vue/runtime-dom@3.4.27': + dependencies: + '@vue/runtime-core': 3.4.27 + '@vue/shared': 3.4.27 + csstype: 3.1.3 + + '@vue/server-renderer@3.4.27(vue@3.4.27(typescript@5.4.5))': + dependencies: + '@vue/compiler-ssr': 3.4.27 + '@vue/shared': 3.4.27 + vue: 3.4.27(typescript@5.4.5) + + '@vue/shared@3.4.27': {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + asynckit@0.4.0: {} + + autoprefixer@10.4.19(postcss@8.4.38): + dependencies: + browserslist: 4.23.0 + caniuse-lite: 1.0.30001621 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.1 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + + axios@1.7.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.23.0: + dependencies: + caniuse-lite: 1.0.30001621 + electron-to-chromium: 1.4.778 + node-releases: 2.0.14 + update-browserslist-db: 1.0.16(browserslist@4.23.0) + + camelcase-css@2.0.1: {} + + caniuse-lite@1.0.30001621: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@4.1.1: {} + + computeds@0.0.1: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-selector-tokenizer@0.8.0: + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + + cssesc@3.0.0: {} + + csstype@3.1.3: {} + + culori@3.3.0: {} + + daisyui@4.11.1(postcss@8.4.38): + dependencies: + css-selector-tokenizer: 0.8.0 + culori: 3.3.0 + picocolors: 1.0.1 + postcss-js: 4.0.1(postcss@8.4.38) + transitivePeerDependencies: + - postcss + + de-indent@1.0.2: {} + + delayed-stream@1.0.0: {} + + didyoumean@1.2.2: {} + + dlv@1.1.3: {} + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.4.778: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + entities@4.5.0: {} + + esbuild@0.20.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + + escalade@3.1.2: {} + + estree-walker@2.0.2: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fastparse@1.1.2: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + follow-redirects@1.15.6: {} + + foreground-child@3.1.1: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fraction.js@4.3.7: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.3.16: + dependencies: + foreground-child: 3.1.1 + jackspeak: 3.1.2 + minimatch: 9.0.4 + minipass: 7.1.1 + path-scurry: 1.11.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.13.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + jackspeak@3.1.2: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.0: {} + + lilconfig@2.1.0: {} + + lilconfig@3.1.1: {} + + lines-and-columns@1.2.4: {} + + lodash.castarray@4.4.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.merge@4.6.2: {} + + lru-cache@10.2.2: {} + + magic-string@0.30.10: + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + + merge2@1.4.1: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@9.0.4: + dependencies: + brace-expansion: 2.0.1 + + minipass@7.1.1: {} + + moment@2.30.1: {} + + muggle-string@0.4.1: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.7: {} + + node-releases@2.0.14: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + path-browserify@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.2.2 + minipass: 7.1.1 + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pirates@4.0.6: {} + + postcss-import@15.1.0(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.38): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.38 + + postcss-load-config@4.0.2(postcss@8.4.38): + dependencies: + lilconfig: 3.1.1 + yaml: 2.4.2 + optionalDependencies: + postcss: 8.4.38 + + postcss-nested@6.0.1(postcss@8.4.38): + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@6.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.38: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + proxy-from-env@1.1.0: {} + + queue-microtask@1.2.3: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + resolve@1.22.8: + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + + rollup@4.18.0: + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.18.0 + '@rollup/rollup-android-arm64': 4.18.0 + '@rollup/rollup-darwin-arm64': 4.18.0 + '@rollup/rollup-darwin-x64': 4.18.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 + '@rollup/rollup-linux-arm-musleabihf': 4.18.0 + '@rollup/rollup-linux-arm64-gnu': 4.18.0 + '@rollup/rollup-linux-arm64-musl': 4.18.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 + '@rollup/rollup-linux-riscv64-gnu': 4.18.0 + '@rollup/rollup-linux-s390x-gnu': 4.18.0 + '@rollup/rollup-linux-x64-gnu': 4.18.0 + '@rollup/rollup-linux-x64-musl': 4.18.0 + '@rollup/rollup-win32-arm64-msvc': 4.18.0 + '@rollup/rollup-win32-ia32-msvc': 4.18.0 + '@rollup/rollup-win32-x64-msvc': 4.18.0 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + semver@7.6.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + source-map-js@1.2.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.3.16 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@3.4.3: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.7 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.1 + postcss: 8.4.38 + postcss-import: 15.1.0(postcss@8.4.38) + postcss-js: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.2(postcss@8.4.38) + postcss-nested: 6.0.1(postcss@8.4.38) + postcss-selector-parser: 6.1.0 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-interface-checker@0.1.13: {} + + typescript@5.4.5: {} + + update-browserslist-db@1.0.16(browserslist@4.23.0): + dependencies: + browserslist: 4.23.0 + escalade: 3.1.2 + picocolors: 1.0.1 + + util-deprecate@1.0.2: {} + + vite@5.2.11: + dependencies: + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.18.0 + optionalDependencies: + fsevents: 2.3.3 + + vue-template-compiler@2.7.16: + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + vue-tsc@2.0.19(typescript@5.4.5): + dependencies: + '@volar/typescript': 2.2.4 + '@vue/language-core': 2.0.19(typescript@5.4.5) + semver: 7.6.2 + typescript: 5.4.5 + + vue@3.4.27(typescript@5.4.5): + dependencies: + '@vue/compiler-dom': 3.4.27 + '@vue/compiler-sfc': 3.4.27 + '@vue/runtime-dom': 3.4.27 + '@vue/server-renderer': 3.4.27(vue@3.4.27(typescript@5.4.5)) + '@vue/shared': 3.4.27 + optionalDependencies: + typescript: 5.4.5 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + yaml@2.4.2: {} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..78be156 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,41 @@ + + + diff --git a/frontend/src/assets/vue.svg b/frontend/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/frontend/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/Devices.vue b/frontend/src/components/Devices.vue new file mode 100644 index 0000000..eef54cb --- /dev/null +++ b/frontend/src/components/Devices.vue @@ -0,0 +1,192 @@ + + + \ No newline at end of file diff --git a/frontend/src/components/Experts.vue b/frontend/src/components/Experts.vue new file mode 100644 index 0000000..97745f3 --- /dev/null +++ b/frontend/src/components/Experts.vue @@ -0,0 +1,288 @@ + + + \ No newline at end of file diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..2425c0f --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/static.go b/frontend/static.go new file mode 100644 index 0000000..5c8c684 --- /dev/null +++ b/frontend/static.go @@ -0,0 +1,6 @@ +package frontend + +import "embed" + +//go:embed dist +var Static embed.FS diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..4e52c1a --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,18 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + daisyui: { + themes: ["light", "dark"], + }, + plugins: [ + require("@tailwindcss/typography"), + require('daisyui'), + ], +} + diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..9e03e60 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..aacf274 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,15 @@ +import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + server: { + proxy: { + '/api': { + target: 'http://localhost:9090', + changeOrigin: true, + } + } + } +}) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0a5b8c8 --- /dev/null +++ b/go.mod @@ -0,0 +1,76 @@ +module dyproxy + +go 1.22.1 + +require ( + github.com/fsnotify/fsnotify v1.7.0 + github.com/go-faker/faker/v4 v4.4.1 + github.com/go-jet/jet/v2 v2.11.1 + github.com/gofiber/fiber/v3 v3.0.0-beta.2 + github.com/gofiber/utils/v2 v2.0.0-beta.4 + github.com/imroc/req/v3 v3.43.5 + github.com/lxn/walk v0.0.0-20210112085537-c389da54e794 + github.com/mattn/go-sqlite3 v1.14.22 + github.com/pkg/errors v0.9.1 + github.com/rogeecn/fabfile v1.4.0 + github.com/samber/lo v1.39.0 + github.com/sirupsen/logrus v1.9.3 + github.com/smartystreets/goconvey v1.8.1 + github.com/spf13/cobra v1.8.0 + github.com/spf13/viper v1.18.2 + github.com/stretchr/testify v1.9.0 + gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153 +) + +require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 // indirect + github.com/elazarl/goproxy/ext v0.0.0-20231117061959-7cc037d33fb5 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/onsi/ginkgo/v2 v2.16.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/quic-go v0.41.0 // indirect + github.com/refraction-networking/utls v1.6.3 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/smarty/assertions v1.15.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.52.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/mock v0.4.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.19.0 // indirect + gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c895f6e --- /dev/null +++ b/go.sum @@ -0,0 +1,178 @@ +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 h1:m62nsMU279qRD9PQSWD1l66kmkXzuYcnVJqL4XLeV2M= +github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/elazarl/goproxy/ext v0.0.0-20231117061959-7cc037d33fb5 h1:iGoePcl8bIDJxxRAL2Q4E4Rt35z5m917RJb8lAvdrQw= +github.com/elazarl/goproxy/ext v0.0.0-20231117061959-7cc037d33fb5/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-faker/faker/v4 v4.4.1 h1:LY1jDgjVkBZWIhATCt+gkl0x9i/7wC61gZx73GTFb+Q= +github.com/go-faker/faker/v4 v4.4.1/go.mod h1:HRLrjis+tYsbFtIHufEPTAIzcZiRu0rS9EYl2Ccwme4= +github.com/go-jet/jet/v2 v2.11.1 h1:SEbh2lRUIiQweJpV0boWsQ4bV13x9p4h+RfajnL6vgM= +github.com/go-jet/jet/v2 v2.11.1/go.mod h1:+DTofDkGp1c0vpooXWEZyNhyi0k0mL7N2W9tdP4YqfA= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao= +github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM= +github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= +github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/imroc/req/v3 v3.43.5 h1:fL7dOEfld+iEv1rwnIxseJz2/Y7JZ/HgbAURLZkat80= +github.com/imroc/req/v3 v3.43.5/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8ToyQc2xA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lxn/walk v0.0.0-20210112085537-c389da54e794 h1:NVRJ0Uy0SOFcXSKLsS65OmI1sgCCfiDUPj+cwnH7GZw= +github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= +github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k= +github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= +github.com/refraction-networking/utls v1.6.3 h1:MFOfRN35sSx6K5AZNIoESsBuBxS2LCgRilRIdHb6fDc= +github.com/refraction-networking/utls v1.6.3/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs= +github.com/rogeecn/fabfile v1.4.0 h1:Rw7/7OH8cV4aRPw79Oa4hHHFKaC/ol+sNmGcB/usHaQ= +github.com/rogeecn/fabfile v1.4.0/go.mod h1:EPwX7TtVcIWSLJkJAqxSzYjM/aV1Q0wymcaXqnMgzas= +github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= +github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc= +gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153 h1:i2sumy6EgvN2dbX7HPhoDc7hLyoym3OYdU5HlvUUrpE= +gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153/go.mod h1:xzjpkyedLMz3EXUTBbkRuuGPsxfsBX3Sy7J6kC9Gvoc= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/init.sql b/init.sql new file mode 100644 index 0000000..09a8b3b --- /dev/null +++ b/init.sql @@ -0,0 +1,49 @@ +CREATE TABLE "main"."expert" ( + "id" INTEGER NOT NULL, + "uid" text NOT NULL DEFAULT '', + "sec_uid" text NOT NULL DEFAULT '', + "short_id" text NOT NULL DEFAULT '', + "real_name" text NOT NULL DEFAULT '', + "nick_name" text NOT NULL DEFAULT '', + "state" text NOT NULL DEFAULT 'enable', + "since" INTEGER NOT NULL DEFAULT 0, + "config" text NOT NULL DEFAULT '', + "config_at" INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "main"."idx_expert_uid" ON "expert" ( + "uid" +); + +CREATE TABLE "main"."follower" ( + "id" INTEGER NOT NULL, + "avatar" TEXT NOT NULL DEFAULT '', + "nickname" TEXT NOT NULL DEFAULT '', + "sec_uid" text NOT NULL DEFAULT '', + "short_id" text NOT NULL DEFAULT '', + "uid" text NOT NULL DEFAULT '', + "unique_id" text NOT NULL DEFAULT '', + "expert_uid" text NOT NULL DEFAULT '', + "followed" INTEGER NOT NULL DEFAULT 0, + "created_at" INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "main"."idx_follower_uid" ON "follower" ( + "uid" +); + +CREATE TABLE "main"."device" ( + "id" INTEGER NOT NULL, + "uuid" text NOT NULL DEFAULT '', + "name" text NOT NULL DEFAULT '', + "expert" text NOT NULL DEFAULT '', + "state" text NOT NULL DEFAULT 'enable', + "note" text NOT NULL DEFAULT '', + PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "main"."idx_device_uuid" ON "device" ( + "uuid" +); \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..61dec9a --- /dev/null +++ b/main.go @@ -0,0 +1,10 @@ +/* +Copyright © 2024 NAME HERE +*/ +package main + +import "dyproxy/cmd" + +func main() { + cmd.Execute() +} diff --git a/modules/.keep b/modules/.keep new file mode 100644 index 0000000..e69de29 diff --git a/modules/proxy/logic.go b/modules/proxy/logic.go new file mode 100644 index 0000000..339ad0d --- /dev/null +++ b/modules/proxy/logic.go @@ -0,0 +1,72 @@ +package proxy + +import ( + "encoding/json" + "regexp" + + "dyproxy/.gen/model" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (p *Proxy) processFollowers(body []byte) { + var follower Follower + if err := json.Unmarshal(body, &follower); err != nil { + err = errors.Wrap(err, "unmarshal followers") + logrus.Error(err) + return + } + + followers := []model.Follower{} + for _, f := range follower.Followers { + m := model.Follower{ + Avatar: f.AvatarThumb.URLList[0], + Nickname: f.Nickname, + SecUID: f.SecUID, + ShortID: f.ShortID, + UID: f.UID, + UniqueID: f.UniqueID, + ExpertUID: follower.MyselfUserID, + } + + logrus.Warnf("follower: %+v", m) + followers = append([]model.Follower{m}, followers...) + } + + // post followers + if _, err := p.client.R().SetBody(followers).Post("/api/followers"); err != nil { + logrus.Error("post /api/followers, ", err) + } +} + +func (p *Proxy) processUserInfo(body []byte) { + pattern := `self.__pace_f.push\(.*?"uid\\":\\"(.*?)\\",.*?\\"secUid\\":\\"(.*?)\\",.*?\\"shortId\\":\\"(.*?)\\",.*\\"realName\\":\\"(.*?)\\",.*?"nickname\\":\\"(.*?)\\",.*?` + reg := regexp.MustCompile(pattern) + + matches := reg.FindSubmatch(body) + if len(matches) == 0 { + logrus.Error("no match users") + return + } + + if len(matches) != 6 { + logrus.Error("invalid match") + return + } + + expert := model.Expert{ + UID: string(matches[1]), + SecUID: string(matches[2]), + ShortID: string(matches[3]), + RealName: string(matches[4]), + NickName: string(matches[5]), + } + + logrus.Warnf("expert: %+v", expert) + + // post user info + if _, err := p.client.R().SetBody(expert).Post("/api/experts"); err != nil { + logrus.Error("post /api/experts, ", err) + } +} diff --git a/modules/proxy/opt_follower.go b/modules/proxy/opt_follower.go new file mode 100644 index 0000000..40d150c --- /dev/null +++ b/modules/proxy/opt_follower.go @@ -0,0 +1,70 @@ +package proxy + +import ( + "bytes" + "io" + "net/http" + + "github.com/sirupsen/logrus" + "gopkg.in/elazarl/goproxy.v1" +) + +type Follower struct { + Extra struct { + FatalItemIds []any `json:"fatal_item_ids"` + Logid string `json:"logid"` + Now int64 `json:"now"` + } `json:"extra"` + Followers []struct { + AvatarThumb struct { + Height int `json:"height"` + URI string `json:"uri"` + URLList []string `json:"url_list"` + Width int `json:"width"` + } `json:"avatar_thumb"` + Nickname string `json:"nickname"` + SecUID string `json:"sec_uid"` + ShortID string `json:"short_id"` + UID string `json:"uid"` + UniqueID string `json:"unique_id"` + UniqueIDModifyTime int `json:"unique_id_modify_time"` + } `json:"followers"` + HasMore bool `json:"has_more"` + MyselfUserID string `json:"myself_user_id"` + Offset int `json:"offset"` + RecHasMore bool `json:"rec_has_more"` + StatusCode int `json:"status_code"` + StorePage string `json:"store_page"` + Total int `json:"total"` + VcdCount int `json:"vcd_count"` +} + +func WithFollower() Option { + return func(p *Proxy) { + p.proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + if resp.StatusCode != 200 { + return resp + } + + if resp.Request.Host != "www.douyin.com" { + return resp + } + + if resp.Request.URL.Path != "/aweme/v1/web/user/follower/list/" { + return resp + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + logrus.Error(err) + return resp + } + resp.Body.Close() + resp.Body = io.NopCloser(bytes.NewReader(body)) + + go p.processFollowers(body) + + return resp + }) + } +} diff --git a/modules/proxy/opt_user_info.go b/modules/proxy/opt_user_info.go new file mode 100644 index 0000000..ec04d06 --- /dev/null +++ b/modules/proxy/opt_user_info.go @@ -0,0 +1,75 @@ +package proxy + +import ( + "bytes" + "fmt" + "io" + "net/http" + + "github.com/sirupsen/logrus" + "gopkg.in/elazarl/goproxy.v1" +) + +type UserInfo struct { + UID string + SecUID string + ShortID string + RealName string + Nickname string +} + +func WithUserInfo(duration int) Option { + return func(p *Proxy) { + p.proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + if resp.StatusCode != 200 { + return resp + } + + if resp.Request.Host != "www.douyin.com" { + return resp + } + + if resp.Request.URL.Path != "/user/self" { + return resp + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + logrus.Error(err) + return resp + } + resp.Body.Close() + + // 添加定时刷新 + codes := ` + + + + + + +` + codes = fmt.Sprintf(codes, duration, duration) + body = bytes.Replace(body, []byte(""), []byte(codes), 1) + resp.Body = io.NopCloser(bytes.NewReader(body)) + + // remove Content-Security-Policy + resp.Header.Del("Content-Security-Policy") + + go p.processUserInfo(body) + + return resp + }) + } +} diff --git a/modules/proxy/serve.go b/modules/proxy/serve.go new file mode 100644 index 0000000..97316b3 --- /dev/null +++ b/modules/proxy/serve.go @@ -0,0 +1,133 @@ +package proxy + +import ( + "context" + "fmt" + "io" + "log" + "net/http" + "strings" + "time" + + _ "embed" + + "github.com/imroc/req/v3" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "gopkg.in/elazarl/goproxy.v1" +) + +func ServeE(cmd *cobra.Command, args []string) error { + duration, err := cmd.Flags().GetInt("duration") + if err != nil { + duration = 10 + } + + host, err := cmd.Flags().GetString("host") + if err != nil { + return err + } + + host = strings.TrimSpace(host) + if host == "" { + logrus.Fatal("host is empty") + } + + logrus.SetLevel(logrus.WarnLevel) + + debug, err := cmd.Flags().GetBool("debug") + if err != nil { + return err + } + + return NewProxy(host, debug, duration).Serve(29999) +} + +func NewProxy(host string, debug bool, duration int) *Proxy { + return New( + WithHost(host), + WithLogger(log.New(io.Discard, "", log.LstdFlags)), + WithHttps(), + WithDebug(debug), + WithVerbose(), + WithFollower(), + WithUserInfo(duration), + ) +} + +type Option func(*Proxy) + +func WithLogger(logger *log.Logger) Option { + return func(p *Proxy) { + p.proxy.Logger = logger + // p.proxy.Logger = log.New(io.Discard, "", log.LstdFlags) + } +} + +func WithVerbose() Option { + return func(p *Proxy) { + p.proxy.Verbose = true + } +} + +func WithHttps() Option { + return func(p *Proxy) { + p.proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) + } +} + +func WithDebug(debug bool) Option { + return func(p *Proxy) { + if debug { + p.client = p.client.DevMode() + logrus.SetLevel(logrus.DebugLevel) + } + } +} + +func WithHost(h string) Option { + return func(p *Proxy) { + logrus.Infof("post data to host: %s", h) + p.client = req.C(). + SetBaseURL(h). + EnableInsecureSkipVerify(). + SetTimeout(10*time.Second). + SetCommonBasicAuth("rogeecn", "xixi@0202") + } +} + +type Proxy struct { + proxy *goproxy.ProxyHttpServer + client *req.Client + server *http.Server +} + +func New(opts ...Option) *Proxy { + proxy := &Proxy{ + proxy: goproxy.NewProxyHttpServer(), + } + + for _, opt := range opts { + opt(proxy) + } + + return proxy +} + +// run +func (p *Proxy) Serve(port uint) error { + logrus.Infof("douyin proxy start serve at: :%d", port) + p.server = &http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: p.proxy, + } + + return p.server.ListenAndServe() +} + +func (p *Proxy) Shutdown() error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + return p.server.Shutdown(ctx) +} diff --git a/modules/web/ca.crt b/modules/web/ca.crt new file mode 100644 index 0000000..62653da --- /dev/null +++ b/modules/web/ca.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD +VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM +B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0 +aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0 +MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE +CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV +BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI +hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9 +3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP +sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9 +V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh +hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr +lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq +j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo +WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD +fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj +YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh +WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj +UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4 +uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F +AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0 +C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3 +Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin +o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye +i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr +bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY +VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft +8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86 +NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV +BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA== +-----END CERTIFICATE----- diff --git a/modules/web/route_device.go b/modules/web/route_device.go new file mode 100644 index 0000000..97ca1e5 --- /dev/null +++ b/modules/web/route_device.go @@ -0,0 +1,262 @@ +package web + +import ( + "errors" + "fmt" + "strconv" + "time" + + "dyproxy/.gen/model" + "dyproxy/.gen/table" + + "github.com/go-jet/jet/v2/qrm" + . "github.com/go-jet/jet/v2/sqlite" + "github.com/gofiber/fiber/v3" + "github.com/samber/lo" + "github.com/sirupsen/logrus" +) + +type ExpertConfig struct { + Voice string + Hello string + Wechat string + Region []string + NameKeyword []string + Produce bool + DefaultName bool + DefaultAvatar bool +} + +func (s *WebServer) routeGetDevices(c fiber.Ctx) error { + devices := []model.Device{} + stmt := table.Device.SELECT(table.Device.AllColumns).ORDER_BY(table.Device.ID) + if err := stmt.QueryContext(c.UserContext(), s.db, &devices); err != nil { + return err + } + + expertsIds := lo.FilterMap(devices, func(device model.Device, _ int) (Expression, bool) { + if device.Expert == "" { + return nil, false + } + + return String(device.Expert), true + }) + + if len(expertsIds) == 0 { + return c.JSON(devices) + } + + type listDevice struct { + model.Device `json:",inline"` + ExpertName string + } + + // find experts by ids + experts := []model.Expert{} + stmt = table.Expert.SELECT(table.Expert.AllColumns).WHERE(table.Expert.UID.IN(expertsIds...)) + if err := stmt.QueryContext(c.UserContext(), s.db, &experts); err != nil { + return err + } + + expertsMap := make(map[string]string) + for _, expert := range experts { + expertsMap[expert.UID] = expert.RealName + } + + list := make([]listDevice, 0, len(devices)) + for _, device := range devices { + if expertName, ok := expertsMap[device.Expert]; ok { + list = append(list, listDevice{ + Device: device, + ExpertName: expertName, + }) + } else { + list = append(list, listDevice{ + Device: device, + ExpertName: "未设置", + }) + } + } + return c.JSON(list) +} + +func (s *WebServer) routeGetDevice(c fiber.Ctx) error { + deviceID := c.Params("uuid") + var device model.Device + err := table.Device.SELECT(table.Device.AllColumns).WHERE(table.Device.UUID.EQ(String(deviceID))).QueryContext(c.UserContext(), s.db, &device) + if err != nil { + if errors.Is(err, qrm.ErrNoRows) { + // create new device + device.UUID = deviceID + _, err = table.Device.INSERT(table.Device.AllColumns.Except(table.Device.ID)).MODEL(device).ExecContext(c.UserContext(), s.db) + if err != nil { + return err + } + return c.JSON(nil) + } + return err + } + + return c.JSON(device) +} + +func (s *WebServer) routeGetDeviceFollower(c fiber.Ctx) error { + if s.pendingItems == nil { + s.pendingItems = make(map[int32]time.Time) + } + + // get device + deviceID := c.Params("uuid") + var device model.Device + err := table.Device.SELECT(table.Device.AllColumns).WHERE(table.Device.UUID.EQ(String(deviceID))).QueryContext(c.UserContext(), s.db, &device) + if err != nil { + if errors.Is(err, qrm.ErrNoRows) { + // create new device + device.UUID = deviceID + _, err = table.Device.INSERT(table.Device.AllColumns.Except(table.Device.ID)).MODEL(device).ExecContext(c.UserContext(), s.db) + if err != nil { + return err + } + return c.JSON(nil) + } + return err + } + + if device.Expert == "" { + return c.JSON(nil) + } + + if device.State == StateStop { + return c.JSON(nil) + } + + tbl := table.Follower + + lastID := c.Query("last", "") + if lastID != "" { + id, err := strconv.Atoi(lastID) + if err != nil { + return err + } + + // remove from pending + s.listLock.Lock() + delete(s.pendingItems, int32(id)) + s.listLock.Unlock() + + tbl. + UPDATE(table.Follower.Followed). + SET(Int32(1)). + WHERE(table.Follower.ID.EQ(Int32(int32(id)))). + ExecContext(c.UserContext(), s.db) + } + + pendingIDs := []Expression{} + for i := range s.pendingItems { + pendingIDs = append(pendingIDs, Int32(i)) + } + + pendingIDs = []Expression{} + + // get device expert + + var expert model.Expert + err = table.Expert.SELECT(table.Expert.AllColumns).WHERE(table.Expert.UID.EQ(String(device.Expert))).QueryContext(c.UserContext(), s.db, &expert) + if err != nil { + return err + } + c.Response().Header.Set("x-expert-uid", expert.UID) + c.Response().Header.Set("x-config-at", fmt.Sprintf("%d", expert.ConfigAt)) + + if expert.State == StateStop { + return c.JSON(nil) + } + + condition := tbl.Followed.EQ(Int32(0)). + AND(tbl.ExpertUID.EQ(String(device.Expert))). + AND(tbl.ID.NOT_IN(pendingIDs...)). + AND(tbl.CreatedAt.GT(Int32(expert.Since))) + + stmt := tbl. + SELECT(tbl.AllColumns). + WHERE(condition). + ORDER_BY(table.Follower.ID.DESC()). + LIMIT(1) + logrus.Debug(stmt.DebugSql()) + + var follower model.Follower + if err := stmt.QueryContext(c.UserContext(), s.db, &follower); err != nil { + if errors.Is(err, qrm.ErrNoRows) { + return c.JSON(nil) + } + return err + } + + s.listLock.Lock() + s.pendingItems[int32(follower.ID)] = time.Now() + s.listLock.Unlock() + + return c.JSON(follower) +} + +// routeSetDeviceExpert +func (s *WebServer) routeSetDeviceExpert(c fiber.Ctx) error { + deviceID := c.Params("uuid") + uid := c.Params("uid") + + type body struct { + Name string `json:"name"` + } + + b := &body{} + if err := c.Bind().JSON(b); err != nil { + return err + } + + var device model.Device + err := table.Device.SELECT(table.Device.AllColumns).WHERE(table.Device.UUID.EQ(String(deviceID))).QueryContext(c.UserContext(), s.db, &device) + if err != nil { + return err + } + + var expert model.Expert + err = table.Expert.SELECT(table.Expert.UID).WHERE(table.Expert.UID.EQ(String(uid))).QueryContext(c.UserContext(), s.db, &expert) + if err != nil { + return err + } + + device.Expert = expert.UID + _, err = table.Device.UPDATE().SET( + table.Device.Expert.SET(String(device.Expert)), + table.Device.Name.SET(String(b.Name)), + ).WHERE(table.Device.UUID.EQ(String(deviceID))).ExecContext(c.UserContext(), s.db) + if err != nil { + return err + } + + return c.JSON(nil) +} + +func (s *WebServer) routePatchDeviceState(c fiber.Ctx) error { + var state struct { + State string `json:"state"` + Note string `json:"note"` + } + if err := c.Bind().JSON(&state); err != nil { + return err + } + + uuid := c.Params("uuid") + + tbl := table.Device + _, err := tbl. + UPDATE(). + SET( + tbl.State.SET(String(state.State)), + tbl.Note.SET(String(state.Note)), + ). + WHERE(tbl.UUID.EQ(String(uuid))). + ExecContext(c.UserContext(), s.db) + + return err +} diff --git a/modules/web/route_expert.go b/modules/web/route_expert.go new file mode 100644 index 0000000..34d280c --- /dev/null +++ b/modules/web/route_expert.go @@ -0,0 +1,200 @@ +package web + +import ( + "encoding/json" + "fmt" + "time" + + "dyproxy/.gen/model" + "dyproxy/.gen/table" + + . "github.com/go-jet/jet/v2/sqlite" + "github.com/gofiber/fiber/v3" + "github.com/sirupsen/logrus" +) + +func (s *WebServer) routeGetExpertConfig(c fiber.Ctx) error { + uid := c.Params("uid", "") + var user model.Expert + err := table.Expert.SELECT(table.Expert.AllColumns).WHERE(table.Expert.UID.EQ(String(uid))).QueryContext(c.UserContext(), s.db, &user) + if err != nil { + return err + } + + if user.Config == "" { + user.Config = "{}" + } + + var config ExpertConfig + if err := json.Unmarshal([]byte(user.Config), &config); err != nil { + return err + } + c.Response().Header.Set("x-config-at", fmt.Sprintf("%d", user.ConfigAt)) + return c.JSON(config) +} + +func (s *WebServer) routeGetExpert(c fiber.Ctx) error { + uid := c.Params("uid", "") + var user model.Expert + err := table.Expert.SELECT(table.Expert.AllColumns).WHERE(table.Expert.UID.EQ(String(uid))).QueryContext(c.UserContext(), s.db, &user) + if err != nil { + return err + } + return c.JSON(user) +} + +func (s *WebServer) routeGetExperts(c fiber.Ctx) error { + stmt := table.Expert.SELECT(table.Expert.AllColumns).ORDER_BY(table.Expert.ID) + + var rows []model.Expert + if err := stmt.Query(s.db, &rows); err != nil { + return err + } + + sql := `SELECT + expert_uid, + COUNT(*) AS total, + SUM(case when followed = 1 then 1 else 0 end) AS followed +FROM + follower +GROUP BY + expert_uid;` + r, err := s.db.QueryContext(c.UserContext(), sql) + if err != nil { + return err + } + + type statistics struct { + UserUID string + Total int32 + Followed int32 + } + + var statisticsItems []statistics + for r.Next() { + item := statistics{} + if err := r.Scan(&item.UserUID, &item.Total, &item.Followed); err != nil { + return err + } + + statisticsItems = append(statisticsItems, item) + } + + type resp struct { + Focus int32 + Total int32 + Conf ExpertConfig + model.Expert `json:",inline"` + } + var users []resp + + for _, row := range rows { + stat := statistics{} + // get item from statisticsItems where row.UID == item.UserUID + for _, item := range statisticsItems { + if row.UID == item.UserUID { + stat = item + break + } + } + + var conf ExpertConfig + if row.Config == "" { + row.Config = "{}" + } + if err := json.Unmarshal([]byte(row.Config), &conf); err != nil { + logrus.Error(err) + continue + } + + row.Config = "" + users = append(users, resp{ + Focus: stat.Followed, + Total: stat.Total, + Conf: conf, + Expert: row, + }) + } + + return c.JSON(users) +} + +func (s *WebServer) routePostExperts(c fiber.Ctx) error { + expert := &model.Expert{} + if err := c.Bind().JSON(expert); err != nil { + return err + } + + tbl := table.Expert + _, err := tbl. + INSERT(tbl.AllColumns.Except(tbl.ID)). + MODEL(expert). + ON_CONFLICT(tbl.UID). + DO_NOTHING(). + ExecContext(c.UserContext(), s.db) + + return err +} + +func (s *WebServer) routePatchExpertConfig(c fiber.Ctx) error { + var data struct { + Since int32 `json:"since"` + ExpertConfig `json:",inline"` + } + if err := c.Bind().JSON(&data); err != nil { + return err + } + + uid := c.Params("uid") + tbl := table.Expert + + // get expert by uid + var expert model.Expert + if err := tbl.SELECT(tbl.AllColumns).WHERE(tbl.UID.EQ(String(uid))).QueryContext(c.UserContext(), s.db, &expert); err != nil { + return err + } + if expert.Config == "" { + expert.Config = "{}" + } + + newExpertConfig, err := json.Marshal(data.ExpertConfig) + if err != nil { + return err + } + + _, err = tbl. + UPDATE(). + SET( + tbl.Config.SET(String(string(newExpertConfig))), + tbl.ConfigAt.SET(Int(time.Now().Unix())), + ). + WHERE(tbl.UID.EQ(String(uid))). + ExecContext(c.UserContext(), s.db) + + return err +} + +const ( + StateNormal = "" + StateStop = "stop" +) + +func (s *WebServer) routePatchExpertState(c fiber.Ctx) error { + var state struct { + State string `json:"state"` + } + if err := c.Bind().JSON(&state); err != nil { + return err + } + + uid := c.Params("uid") + + tbl := table.Expert + _, err := tbl. + UPDATE(). + SET(tbl.State.SET(String(state.State))). + WHERE(tbl.UID.EQ(String(uid))). + ExecContext(c.UserContext(), s.db) + + return err +} diff --git a/modules/web/route_follower.go b/modules/web/route_follower.go new file mode 100644 index 0000000..770a861 --- /dev/null +++ b/modules/web/route_follower.go @@ -0,0 +1,109 @@ +package web + +import ( + "errors" + "strconv" + "time" + + "dyproxy/.gen/model" + "dyproxy/.gen/table" + + "github.com/go-jet/jet/v2/qrm" + . "github.com/go-jet/jet/v2/sqlite" + "github.com/gofiber/fiber/v3" + "github.com/sirupsen/logrus" +) + +func (s *WebServer) routeGetFollower(c fiber.Ctx) error { + if s.pendingItems == nil { + s.pendingItems = make(map[int32]time.Time) + } + + tbl := table.Follower + + lastID := c.Query("last", "") + if lastID != "" { + id, err := strconv.Atoi(lastID) + if err != nil { + return err + } + + // remove from pending + s.listLock.Lock() + delete(s.pendingItems, int32(id)) + s.listLock.Unlock() + + tbl. + UPDATE(table.Follower.Followed). + SET(Int32(1)). + WHERE(table.Follower.ID.EQ(Int32(int32(id)))). + ExecContext(c.UserContext(), s.db) + } + + uid := c.Params("uid") + + pendingIDs := []Expression{} + for i := range s.pendingItems { + pendingIDs = append(pendingIDs, Int32(i)) + } + + var expert model.Expert + err := table.Expert.SELECT(table.Expert.State, table.Expert.Since).WHERE(table.Expert.UID.EQ(String(uid))).QueryContext(c.UserContext(), s.db, &expert) + if err != nil { + return err + } + + if expert.State == StateStop { + return c.JSON(nil) + } + + condition := tbl.Followed.EQ(Int32(0)). + AND(tbl.ExpertUID.EQ(String(uid))). + AND(tbl.ID.NOT_IN(pendingIDs...)). + AND(tbl.CreatedAt.GT(Int32(expert.Since))) + + stmt := tbl. + SELECT(tbl.AllColumns). + WHERE(condition). + ORDER_BY(table.Follower.ID.DESC()). + LIMIT(1) + logrus.Debug(stmt.DebugSql()) + + var follower model.Follower + if err := stmt.QueryContext(c.UserContext(), s.db, &follower); err != nil { + if errors.Is(err, qrm.ErrNoRows) { + return c.JSON(nil) + } + return err + } + + s.listLock.Lock() + s.pendingItems[int32(follower.ID)] = time.Now() + s.listLock.Unlock() + + return c.JSON(follower) +} + +func (s *WebServer) routePostFollower(c fiber.Ctx) error { + followers := []model.Follower{} + if err := c.Bind().JSON(&followers); err != nil { + return err + } + + tbl := table.Follower + + for _, f := range followers { + f.CreatedAt = int32(time.Now().In(s.local).Unix()) + _, err := tbl. + INSERT(tbl.AllColumns.Except(tbl.ID)). + MODEL(f). + ON_CONFLICT(tbl.UID). + DO_NOTHING(). + ExecContext(c.UserContext(), s.db) + if err != nil { + logrus.Error(err) + } + } + + return nil +} diff --git a/modules/web/route_index.go b/modules/web/route_index.go new file mode 100644 index 0000000..0494280 --- /dev/null +++ b/modules/web/route_index.go @@ -0,0 +1,18 @@ +package web + +import ( + "os" + + "github.com/gofiber/fiber/v3" +) + +func (s *WebServer) routeCss(c fiber.Ctx) error { + b, _ := os.ReadFile("./modules/web/dst/style.css") + c.Set("Content-Type", "text/css") + return c.Send(b) +} + +// routeIndex +func (s *WebServer) routeIndex(c fiber.Ctx) error { + return c.SendString("Hello 👋!") +} diff --git a/modules/web/route_version.go b/modules/web/route_version.go new file mode 100644 index 0000000..8e0d490 --- /dev/null +++ b/modules/web/route_version.go @@ -0,0 +1,71 @@ +package web + +import ( + "io/fs" + "path/filepath" + "strings" + + "github.com/gofiber/fiber/v3" + "github.com/gofiber/utils/v2" +) + +func (s *WebServer) routeGetVersion(c fiber.Ctx) error { + files := []string{} + error := filepath.WalkDir(config.Path, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + if strings.HasPrefix(path, filepath.Join(config.Path, ".git/")) { + return nil + } + + if strings.HasPrefix(path, filepath.Join(config.Path, "boot/")) { + return nil + } + + if strings.ToLower(path) == strings.ToLower(filepath.Join(config.Path, "README.md")) { + return nil + } + + if strings.ToLower(path) == strings.ToLower(filepath.Join(config.Path, "main.js")) { + return nil + } + + if strings.ToLower(path) == strings.ToLower(filepath.Join(config.Path, "project.json")) { + return nil + } + + if path == config.Path { + return nil + } + + files = append(files, strings.Replace(path, config.Path+"/", "", -1)) + return nil + }) + if error != nil { + return error + } + + files = append(files, "version.txt") + return c.JSON(map[string]interface{}{ + "version": config.Version, + "files": files, + }) +} + +func (s *WebServer) routeGetVersionFile(c fiber.Ctx) error { + file := c.Params("+") + c.Response().Header.SetCanonical( + utils.UnsafeBytes(fiber.HeaderContentDisposition), + utils.UnsafeBytes(`attachment;`), + ) + if file == "version.txt" { + return c.SendString(config.Version) + } + return c.SendFile(filepath.Join(config.Path, file), false) +} diff --git a/modules/web/routes.go b/modules/web/routes.go new file mode 100644 index 0000000..07cef0f --- /dev/null +++ b/modules/web/routes.go @@ -0,0 +1,66 @@ +package web + +import ( + "dyproxy/frontend" + + _ "embed" + + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/basicauth" + "github.com/gofiber/fiber/v3/middleware/filesystem" + "github.com/gofiber/fiber/v3/middleware/redirect" +) + +//go:embed ca.crt +var ca []byte + +func WithRoutes() Option { + return func(s *WebServer) { + apiGroup := s.engine.Group("/api", basicauth.New(basicauth.Config{ + Users: users, + })) + apiGroup.Get("/version", s.routeGetVersion) + apiGroup.Get("/version/file/+", s.routeGetVersionFile) + + apiGroup.Get("/experts", s.routeGetExperts) + apiGroup.Get("/experts/:uid", s.routeGetExpert) + apiGroup.Post("/experts", s.routePostExperts) + apiGroup.Get("/experts/:uid/config", s.routeGetExpertConfig) + apiGroup.Patch("/experts/:uid/config", s.routePatchExpertConfig) + apiGroup.Patch("/experts/:uid/state", s.routePatchExpertState) + + apiGroup.Get("/experts/:uid/follower", s.routeGetFollower) + apiGroup.Post("/followers", s.routePostFollower) + + apiGroup.Get("/devices", s.routeGetDevices) + apiGroup.Get("/devices/:uuid", s.routeGetDevice) + apiGroup.Get("/devices/:uuid/follower", s.routeGetDeviceFollower) + apiGroup.Patch("/devices/:uuid/experts/:uid", s.routeSetDeviceExpert) + apiGroup.Patch("/devices/:uuid/state", s.routePatchDeviceState) + apiGroup.Post("/devices/:uuid/block", s.routePatchDeviceState) + + s.engine.Get("/ca", func(c fiber.Ctx) error { + // send attach ment ca.crt from embeded file + c.Set(fiber.HeaderContentType, "application/x-x509-ca-cert") + c.Set(fiber.HeaderContentDisposition, "attachment; filename=ca.crt") + return c.Send(ca) + }) + + s.engine.Use(redirect.New(redirect.Config{ + Rules: map[string]string{"/": "/index.html"}, + StatusCode: 301, + })) + + s.engine.Static("/static", config.Static, fiber.Static{ + Compress: true, + ByteRange: true, + Download: true, + }) + + s.engine.Use(filesystem.New(filesystem.Config{ + Root: frontend.Static, + PathPrefix: "dist", + Index: "/dist/index.html", + })) + } +} diff --git a/modules/web/routes_test.go b/modules/web/routes_test.go new file mode 100644 index 0000000..0b025e6 --- /dev/null +++ b/modules/web/routes_test.go @@ -0,0 +1,267 @@ +package web + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http/httptest" + "testing" + "time" + + "dyproxy/.gen/model" + "dyproxy/.gen/table" + "dyproxy/providers/db" + + "github.com/go-faker/faker/v4" + . "github.com/go-jet/jet/v2/sqlite" + "github.com/rogeecn/fabfile" + "github.com/sirupsen/logrus" + . "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/suite" +) + +type WebTestSuite struct { + suite.Suite + + web *WebServer +} + +func TestWebServer(t *testing.T) { + db, err := db.Connect(fabfile.MustFind("data.db")) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + app := New( + WithDB(db), + WithLogger(), + WithRecover(), + WithRoutes(), + WithPendingCleaner(), + ) + + suite.Run(t, &WebTestSuite{web: app}) +} + +func (s *WebTestSuite) Test_index() { + Convey("Test_index", s.T(), func() { + req := httptest.NewRequest("GET", "http://localhost/", nil) + + resp, err := s.web.engine.Test(req) + So(err, ShouldBeNil) + + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + So(err, ShouldBeNil) + + s.T().Logf("BODY: %s", body) + }) +} + +func (s *WebTestSuite) Test_css() { + Convey("Test_css", s.T(), func() { + req := httptest.NewRequest("GET", "http://localhost/style.css", nil) + + resp, err := s.web.engine.Test(req) + So(err, ShouldBeNil) + + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + So(err, ShouldBeNil) + + s.T().Logf("BODY: %s", body) + }) +} + +func (s *WebTestSuite) Test_Expert() { + Convey("Expert", s.T(), func() { + Convey("GET", func() { + t := table.Expert + db.Truncate(s.web.db, t.TableName()) + + var m model.Expert + So(faker.FakeData(&m), ShouldBeNil) + m.Since = 0 + m.UID = "110" + _, err := t.INSERT().MODEL(m).Exec(s.web.db) + So(err, ShouldBeNil) + + req := httptest.NewRequest("GET", "http://localhost/api/experts/110", nil) + + resp, err := s.web.engine.Test(req) + So(err, ShouldBeNil) + + body, err := io.ReadAll(resp.Body) + defer resp.Body.Close() + So(err, ShouldBeNil) + + s.T().Logf("BODY: %s", body) + }) + }) +} + +func (s *WebTestSuite) Test_Experts() { + Convey("Experts", s.T(), func() { + Convey("GET", func() { + t := table.Expert + db.Truncate(s.web.db, t.TableName()) + + var m model.Expert + So(faker.FakeData(&m), ShouldBeNil) + m.Since = 0 + _, err := t.INSERT().MODEL(m).Exec(s.web.db) + So(err, ShouldBeNil) + + req := httptest.NewRequest("GET", "http://localhost/api/experts", nil) + + resp, err := s.web.engine.Test(req) + So(err, ShouldBeNil) + + body, err := io.ReadAll(resp.Body) + defer resp.Body.Close() + So(err, ShouldBeNil) + + s.T().Logf("BODY: %s", body) + }) + + Convey("PATCH", func() { + t := table.Expert + db.Truncate(s.web.db, t.TableName()) + + var m model.Expert + So(faker.FakeData(&m), ShouldBeNil) + m.ID = 1 + m.UID = "110" + m.Since = 0 + _, err := t.INSERT().MODEL(m).Exec(s.web.db) + So(err, ShouldBeNil) + + req := httptest.NewRequest("PATCH", "http://localhost/api/experts/110/date", bytes.NewReader([]byte(`{"since": 1}`))) + req.Header.Set("Content-Type", "application/json") + + _, err = s.web.engine.Test(req) + So(err, ShouldBeNil) + + // body, err := io.ReadAll(resp.Body) + // defer resp.Body.Close() + // So(err, ShouldBeNil) + + // s.T().Logf("BODY: %s", body) + + var u model.Expert + err = t.SELECT(t.AllColumns).WHERE(t.ID.EQ(Int32(1))).Query(s.web.db, &u) + So(err, ShouldBeNil) + So(u.Since, ShouldEqual, 1) + }) + + Convey("POST", func() { + t := table.Expert + db.Truncate(s.web.db, t.TableName()) + + var m model.Expert + So(faker.FakeData(&m), ShouldBeNil) + m.UID = "110" + + b, _ := json.Marshal(m) + req := httptest.NewRequest("POST", "http://localhost/api/experts", bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + + _, err := s.web.engine.Test(req) + So(err, ShouldBeNil) + + var u model.Expert + err = t.SELECT(t.AllColumns).ORDER_BY(t.ID.DESC()).LIMIT(1).Query(s.web.db, &u) + So(err, ShouldBeNil) + So(u.UID, ShouldEqual, "110") + }) + }) +} + +func (s *WebTestSuite) Test_Follower() { + logrus.SetLevel(logrus.DebugLevel) + FocusConvey("Follower", s.T(), func() { + Convey("GET", func() { + tu := table.Expert + t := table.Follower + db.Truncate(s.web.db, tu.TableName()) + db.Truncate(s.web.db, t.TableName()) + + s.web.pendingItems = make(map[int32]time.Time) + + var m model.Expert + So(faker.FakeData(&m), ShouldBeNil) + m.UID = "110" + m.Since = 0 + _, err := tu.INSERT().MODEL(m).Exec(s.web.db) + So(err, ShouldBeNil) + + var f model.Follower + So(faker.FakeData(&f), ShouldBeNil) + f.ExpertUID = "110" + f.UID = "10" + f.Followed = 0 + _, err = t.INSERT().MODEL(f).Exec(s.web.db) + So(err, ShouldBeNil) + + req := httptest.NewRequest("GET", "http://localhost/api/experts/110/follower", nil) + + resp, err := s.web.engine.Test(req) + So(err, ShouldBeNil) + + body, err := io.ReadAll(resp.Body) + defer resp.Body.Close() + So(err, ShouldBeNil) + + s.T().Logf("BODY: %s", body) + }) + + FocusConvey("POST", func() { + tu := table.Expert + t := table.Follower + db.Truncate(s.web.db, tu.TableName()) + db.Truncate(s.web.db, t.TableName()) + + s.web.pendingItems = make(map[int32]time.Time) + + var m model.Expert + So(faker.FakeData(&m), ShouldBeNil) + m.UID = "110" + m.Since = 0 + _, err := tu.INSERT().MODEL(m).Exec(s.web.db) + So(err, ShouldBeNil) + + fs := []model.Follower{} + var f model.Follower + for i := 0; i < 5; i++ { + So(faker.FakeData(&f), ShouldBeNil) + f.ExpertUID = "110" + f.UID = fmt.Sprintf("%d", 10+i) + f.Followed = 0 + + fs = append(fs, f) + fs = append(fs, f) + } + + b, _ := json.Marshal(fs) + req := httptest.NewRequest("POST", "http://localhost/api/followers", bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + + _, err = s.web.engine.Test(req) + So(err, ShouldBeNil) + + var result struct { + Count int32 + } + stmt := t.SELECT(COUNT(t.ID).AS("Count")) + s.T().Log(stmt.DebugSql()) + err = stmt.Query(s.web.db, &result) + s.T().Logf("%+v", result) + So(err, ShouldBeNil) + So(result.Count, ShouldEqual, 5) + }) + }) +} diff --git a/modules/web/serve.go b/modules/web/serve.go new file mode 100644 index 0000000..bf0aecc --- /dev/null +++ b/modules/web/serve.go @@ -0,0 +1,156 @@ +package web + +import ( + "database/sql" + "fmt" + "log" + "sync" + "time" + + "dyproxy/providers/db" + + "github.com/fsnotify/fsnotify" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/gofiber/fiber/v3/middleware/recover" + "github.com/rogeecn/fabfile" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var users map[string]string = map[string]string{ + "rogeecn": "xixi@0202", +} + +type Config struct { + Version string + Path string + Static string +} + +var config *Config + +func load(f string) error { + viper.SetConfigFile(f) + viper.SetConfigType("yaml") + + if err := viper.ReadInConfig(); err != nil { + return err + } + + if err := viper.Unmarshal(&config); err != nil { + return err + } + + viper.WatchConfig() + viper.OnConfigChange(func(e fsnotify.Event) { + if e.Op != fsnotify.Write { + return + } + + if err := viper.Unmarshal(&config); err != nil { + log.Println(err) + } + log.Printf("config changed: %+v", config) + }) + + return nil +} + +func ServeE(cmd *cobra.Command, args []string) error { + logrus.SetLevel(logrus.WarnLevel) + + conf, err := cmd.Flags().GetString("config") + if err != nil { + return err + } + + if err := load(conf); err != nil { + return err + } + + db, err := db.Connect(fabfile.MustFind("data.db")) + if err != nil { + return err + } + defer db.Close() + + return New( + WithDB(db), + WithLogger(), + WithRecover(), + WithRoutes(), + WithPendingCleaner(), + ).Serve(9090) +} + +type Option func(*WebServer) + +func WithDB(db *sql.DB) Option { + return func(p *WebServer) { + p.db = db + } +} + +func WithLogger() Option { + return func(s *WebServer) { + s.engine.Use(logger.New()) + } +} + +// WithRecover +func WithRecover() Option { + return func(s *WebServer) { + s.engine.Use(recover.New()) + } +} + +type WebServer struct { + db *sql.DB + engine *fiber.App + + local *time.Location + pendingItems map[int32]time.Time + listLock sync.RWMutex +} + +func New(opts ...Option) *WebServer { + cstSh, _ := time.LoadLocation("Asia/Shanghai") + s := &WebServer{ + engine: fiber.New(), + local: cstSh, + } + + for _, opt := range opts { + opt(s) + } + + return s +} + +// run +func (p *WebServer) Serve(port uint) error { + log.Printf("server start serve at: :%d", port) + return p.engine.Listen(fmt.Sprintf(":%d", port)) +} + +func WithPendingCleaner() Option { + return func(s *WebServer) { + if s.pendingItems == nil { + s.pendingItems = make(map[int32]time.Time) + } + + go func() { + for range time.NewTicker(time.Minute * 1).C { + s.listLock.Lock() + for k, v := range s.pendingItems { + if time.Since(v) > time.Minute*2 { + delete(s.pendingItems, k) + } + } + s.listLock.Unlock() + } + }() + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..786d526 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "axios": "^1.7.2", + "moment": "^2.30.1" + } +} diff --git a/pkg/utils/ptr.go b/pkg/utils/ptr.go new file mode 100644 index 0000000..38588e0 --- /dev/null +++ b/pkg/utils/ptr.go @@ -0,0 +1,10 @@ +package utils + +func BytesToStringPtr(b []byte) *string { + s := string(b) + return &s +} + +func StrPtr(s string) *string { + return &s +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..78117fa --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,95 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + axios: + specifier: ^1.7.2 + version: 1.7.2 + moment: + specifier: ^2.30.1 + version: 2.30.1 + +packages: + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, tarball: https://npm.hub.118848.xyz/repository/npm/asynckit/-/asynckit-0.4.0.tgz} + + axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==, tarball: https://npm.hub.118848.xyz/repository/npm/axios/-/axios-1.7.2.tgz} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, tarball: https://npm.hub.118848.xyz/repository/npm/combined-stream/-/combined-stream-1.0.8.tgz} + engines: {node: '>= 0.8'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, tarball: https://npm.hub.118848.xyz/repository/npm/delayed-stream/-/delayed-stream-1.0.0.tgz} + engines: {node: '>=0.4.0'} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==, tarball: https://npm.hub.118848.xyz/repository/npm/follow-redirects/-/follow-redirects-1.15.6.tgz} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==, tarball: https://npm.hub.118848.xyz/repository/npm/form-data/-/form-data-4.0.0.tgz} + engines: {node: '>= 6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, tarball: https://npm.hub.118848.xyz/repository/npm/mime-db/-/mime-db-1.52.0.tgz} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, tarball: https://npm.hub.118848.xyz/repository/npm/mime-types/-/mime-types-2.1.35.tgz} + engines: {node: '>= 0.6'} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==, tarball: https://npm.hub.118848.xyz/repository/npm/moment/-/moment-2.30.1.tgz} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, tarball: https://npm.hub.118848.xyz/repository/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz} + +snapshots: + + asynckit@0.4.0: {} + + axios@1.7.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + delayed-stream@1.0.0: {} + + follow-redirects@1.15.6: {} + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + moment@2.30.1: {} + + proxy-from-env@1.1.0: {} diff --git a/providers/db/sqlite.go b/providers/db/sqlite.go new file mode 100644 index 0000000..3be975d --- /dev/null +++ b/providers/db/sqlite.go @@ -0,0 +1,24 @@ +package db + +import ( + "database/sql" + + _ "github.com/mattn/go-sqlite3" +) + +func Truncate(db *sql.DB, table string) { + db.Exec("DELETE FROM " + table + ";") + db.Exec("UPDATE SQLITE_SEQUENCE SET seq = 0 WHERE name = '" + table + "';") +} + +func Connect(file string) (*sql.DB, error) { + if file == "" { + file = "data.db" + } + + db, err := sql.Open("sqlite3", file) + if err != nil { + return nil, err + } + return db, nil +} diff --git a/robot/audio/caixueying.m4a b/robot/audio/caixueying.m4a new file mode 100644 index 0000000..103efa8 Binary files /dev/null and b/robot/audio/caixueying.m4a differ diff --git a/robot/audio/chenhong.m4a b/robot/audio/chenhong.m4a new file mode 100644 index 0000000..d14251b Binary files /dev/null and b/robot/audio/chenhong.m4a differ diff --git a/robot/audio/dubaojun.m4a b/robot/audio/dubaojun.m4a new file mode 100644 index 0000000..f67b9c4 Binary files /dev/null and b/robot/audio/dubaojun.m4a differ diff --git a/robot/audio/genghonghai.m4a b/robot/audio/genghonghai.m4a new file mode 100644 index 0000000..03531d8 Binary files /dev/null and b/robot/audio/genghonghai.m4a differ diff --git a/robot/audio/liliuji.m4a b/robot/audio/liliuji.m4a new file mode 100644 index 0000000..e4c4198 Binary files /dev/null and b/robot/audio/liliuji.m4a differ diff --git a/robot/audio/liudong.m4a b/robot/audio/liudong.m4a new file mode 100644 index 0000000..d14251b Binary files /dev/null and b/robot/audio/liudong.m4a differ diff --git a/robot/audio/liulianqi.m4a b/robot/audio/liulianqi.m4a new file mode 100644 index 0000000..803b1fc Binary files /dev/null and b/robot/audio/liulianqi.m4a differ diff --git a/robot/audio/wangguozhong.m4a b/robot/audio/wangguozhong.m4a new file mode 100644 index 0000000..6addaf0 Binary files /dev/null and b/robot/audio/wangguozhong.m4a differ diff --git a/robot/audio/xueyongdong.m4a b/robot/audio/xueyongdong.m4a new file mode 100644 index 0000000..47db534 Binary files /dev/null and b/robot/audio/xueyongdong.m4a differ diff --git a/robot/audio/yaobaosen.m4a b/robot/audio/yaobaosen.m4a new file mode 100644 index 0000000..320ba6b Binary files /dev/null and b/robot/audio/yaobaosen.m4a differ diff --git a/robot/audio/zhangguangsheng.m4a b/robot/audio/zhangguangsheng.m4a new file mode 100644 index 0000000..bfcf68a Binary files /dev/null and b/robot/audio/zhangguangsheng.m4a differ diff --git a/robot/audio/zhangzhaofa.m4a b/robot/audio/zhangzhaofa.m4a new file mode 100644 index 0000000..a10cb44 Binary files /dev/null and b/robot/audio/zhangzhaofa.m4a differ diff --git a/robot/lib.js b/robot/lib.js new file mode 100644 index 0000000..831b812 --- /dev/null +++ b/robot/lib.js @@ -0,0 +1,545 @@ +let _global_duration = 1000; + +let global = { + setDuration: function (duration) { + _global_duration = duration + }, +} + +let store = storages.create("_##_@_DY_Proxy"); + +const project = "autojs" + +let pwdFile = function (path) { + let pwd = engines.myEngine().cwd() + + if (!path) { + return pwd + } + + if (path) { + path = path.replace(/^\//, "") + } + + return `${pwd}/${project}/${path}`.replace(/\/\//g, "/").replace(/\/$/, "") +} + +let pageUser = { + activity: "com.ss.android.ugc.aweme.profile.ui.UserProfileActivity", + wait: function () { + waitForActivity(this.activity) + }, + open: function () { + let activity = "snssdk1128://user/profile" + app.startActivity({ + packageName: "com.ss.android.ugc.aweme", + action: "android.intent.action.VIEW", + data: activity, + }); + let appId = id("com.miui.securitycore:id/app1").findOne(1000) + if (appId) { + appId.click() + } + + waitForActivity("com.ss.android.ugc.aweme.main.MainActivity") + + let pt = className("android.widget.TextView").find().findOne(text("我")).bounds() + click(pt.centerX(), pt.centerY()) + }, + openUser: function (uid) { + let activity = "snssdk1128://user/profile/{uid}".replace("{uid}", uid) + app.startActivity({ + packageName: "com.ss.android.ugc.aweme", + action: "android.intent.action.VIEW", + data: activity, + }); + let appId = id("com.miui.securitycore:id/app1").findOne(1000) + if (appId) { + appId.click() + } + waitForActivity("com.ss.android.ugc.aweme.profile.ui.UserProfileActivity") + }, + hasPublishItems: function () { + let txt = "还没有作品" + let id = "com.ss.android.ugc.aweme:id/title" + + let elem = text(txt).findOne(2 * 1000) + if (!elem) { + return true + } + + return !(elem.id() == id) + }, + + getBtnGuanZhu: function () { + let txt = "关注" + let _id = "r2s" + + let elems = text(txt).find() + if (elems.empty()) { + log("找不到关注按钮") + return false + } + + let elem = elems.findOne(id(_id)) + if (!elem) { + log("找不到关注按钮 id") + return false + } + + return elem + }, + + getBtnSiXin: function () { + let txt = "私信" + let desc = "私信" + + let elem = text(txt).findOne(2 * this.duration) + if (!elem) { + log("找不到私信按钮") + return false + } + + if (elem.desc() != desc) { + log("私信按钮 desc 不匹配") + return false + } + return elem + }, + + getIPLocation: function () { + let txt = "IP:" + let _pid = "com.ss.android.ugc.aweme:id/tpw" + + let elems = textStartsWith(txt).find() + if (elems.empty()) { + log("找不到 IP 标签") + return false + } + let elem = null + elems.forEach(function (e) { + if (elem) return + if (e.parent().id() == _pid) { + elem = e + } + }) + if (!elem) { + log("找不到 IP 标签 id") + return false + } + + return elem.text().replace(txt, "").trim() + }, + + clickBtnSiXin: function () { + let elem = this.getBtnSiXin() + if (!elem) { + return false + } + + elem.click() + sleep(2 * this.duration) + return true + }, + + clickBtnGuanZhu: function () { + let elem = this.getBtnGuanZhu() + if (!elem) { + return false + } + + elem.click() + sleep(2 * this.duration) + return true + }, +} + +let pageChat = { + getBtnVoice: function () { + let _desc = "语音" + let _class = "android.widget.Button" + + let elem = className(_class).find().findOne(desc(_desc)) + if (!elem) { + log("找不到 语音 按钮") + return false + } + return elem + }, + getBtnMyLoveSend: function () { + let _txt = "发送" + let _id = "com.ss.android.ugc.aweme:id/send" + + let elem = id(_id).findOne(2 * _global_duration) + if (!elem) { + log("找不到发送按钮") + return false + } + + return elem + }, + getMyLoveFirstProduct: function () { + let _class = "com.bytedance.ies.dmt.ui.widget.DmtTextView"; + let loves = className(_class).find() + if (loves.empty()) { + log("找不到我的喜欢") + return false + } + + return loves.shift() + }, + getMyLoveButton: function () { + let _txt = "我的喜欢" + let _class = "android.widget.TextView" + + let _pclass = "android.view.ViewGroup" + + let elem = className(_pclass).find().findOne(text(_txt)).parent() + if (!elem) { + log("找不到 我的喜欢 按钮") + return false + } + + return elem + }, + getPanelMore: function () { + let _class = "androidx.viewpager.widget.ViewPager" + + let elem = className(_class).findOne(2 * _global_duration) + if (!elem) { + log("找不到 更多面板") + return false + } + + return elem + }, + getBtnCloseMore: function () { + let _desc = "关闭面板" + let _class = "android.widget.Button" + + let elem = className(_class).find().findOne(desc(_desc)) + if (!elem) { + log("找不到关闭面板按钮") + return false + } + + return elem + }, + getBtnMore: function () { + let _desc = "更多面板" + let _class = "android.widget.Button" + + let elem = className(_class).find().findOne(desc(_desc)) + if (!elem) { + log("找不到更多面板按钮") + return false + } + log(elem.id()) + + return elem + }, + clickBtnCloseMore: function () { + let elem = this.getBtnCloseMore() + if (!elem) { + return false + } + + elem.click() + sleep(_global_duration) + return true + }, + clickBtnMore: function () { + let elem = this.getBtnMore() + if (!elem) { + return false + } + + elem.click() + sleep(_global_duration) + return true + }, + swipeMorePanelRight: function () { + let elem = this.getPanelMore() + if (!elem) { + return false + } + + // elem.scrollLeft() + let x = elem.bounds().centerX() + elem.bounds().width() / 4 + let y = elem.bounds().centerY() + let x1 = elem.bounds().left + let y1 = elem.bounds().centerY() + log(x, y, x1, y1) + swipe(x, y, x1, y1, 200) + sleep(_global_duration) + return true + }, + swipeMorePanelLeft: function () { + let elem = this.getPanelMore() + if (!elem) { + return false + } + + let x = elem.bounds().centerX() - elem.bounds().width() / 4 + let y = elem.bounds().centerY() + let x1 = elem.bounds().right + let y1 = elem.bounds().centerY() + log(x, y, x1, y1) + swipe(x, y, x1, y1, 200) + sleep(_global_duration) + return true + }, + + clickBtnMyLove: function () { + let elem = this.getMyLoveButton() + if (!elem) { + return false + } + + elem.click() + // click(elem.bounds().centerX(), elem.bounds().centerY()) + sleep(_global_duration) + return true + }, + clickBtnMyLoveFirstProduct: function () { + let elem = this.getMyLoveFirstProduct() + if (!elem) { + return false + } + + log(elem.className()) + click(elem.bounds().centerX(), elem.bounds().centerY()) + sleep(_global_duration) + return true + }, + clickBtnMyLoveSend: function () { + let elem = this.getBtnMyLoveSend() + if (!elem) { + return false + } + + elem.click() + sleep(_global_duration) + return true + }, + pressBtnVoice: function (duration) { + let elem = this.getBtnVoice() + if (!elem) { + return false + } + + press(elem.bounds().centerX(), elem.bounds().centerY(), duration) + sleep(_global_duration) + return true + }, + sendAudioVoice: function (voice) { + let elem = this.getBtnVoice() + if (!elem) { + log("找不到 语音 按钮") + return false + } + + var musicDuration = threads.disposable(); + threads.start(function () { + sleep(500) + //播放音乐 + media.playMusic(pwdFile(`audio/${voice}.m4a`)); + media.pauseMusic() + + //让音乐播放完 + let duration = media.getMusicDuration() + musicDuration.setAndNotify(duration) + sleep(500) + media.resumeMusic() + + sleep(duration); + }); + + let duration = musicDuration.blockedGet() + + press(elem.bounds().centerX(), elem.bounds().centerY(), 1000 + duration) + sleep(_global_duration) + return true + + }, + isSendVoiceMode: function () { + let _txt = "按住 说话" + return text(_txt).findOne(2 * _global_duration) + }, + getBtnVoiceToTxt: function () { + return desc("文本").findOne(2 * _global_duration) + }, + getInputSendTxt: function () { + return text("发送消息").findOne(2 * _global_duration) + }, + getBtnSendTxt: function () { + return desc('发送').findOne(2 * _global_duration) + }, + sendTxt: function (txt) { + if (this.isSendVoiceMode()) { + let elem = this.getBtnVoiceToTxt() + if (!elem) { + return false + } + elem.click() + sleep(_global_duration) + } + + elem = this.getInputSendTxt() + if (!elem) { + log("找不到发送消息输入框") + return false + } + + elem.setText(txt) + sleep(2 * _global_duration) + + if (!this.getBtnSendTxt().click()) { + log("找不到发送按钮") + return false + } + return true + } +} + +let pageFriend = { + activity: "com.ss.android.ugc.aweme.following.ui.FollowRelationTabActivity", + wait: function () { + lib.waitForActivity(_activity) + }, + open: function () { + // app.startActivity(this.activity) + }, + listFriends: function () { + let _id = "com.ss.android.ugc.aweme:id/content" + let elems = id(_id).find() + if (elems.empty()) { + log("找不到 好友列表") + return [] + } + + let friends = []; + elems.forEach((elem, i) => { + let [name, online] = elem.desc().split(",") + + let btn = elem.findOne(text("发私信")) + log(name, online, btn) + friends.push({ + name: name, + online: online, + btn: btn, + }) + }) + + return friends + }, +} + +let messages = [ + [ + "你好,是要男性健康方面的问题吗", + "{voice}", + "十我详谈吧,这里敏感词太多", + "这里敏感词多", + "这里有时候不常在,消息回复不及时", + ], + [ + "{wechat}" + ], + [ + "↑微訫号", + "微訫号", + "↑微訫号", + "↑徽沁", + "↑微沁", + ], + [ + "十了说一下给你通过", + "加上一对一给你解答", + "", + "加上详谈", + "加上详细沟通一下你的情况", + ] +]; + +let st = { + times: function () { + return store.get('__times', 0) + }, + addTimes: function () { + return store.put('__times', this.times() + 1) + }, + host: function () { + let host = store.get("__host", "") + log("HOST=", host) + if (host.length == 0) { + host = "http://10.1.1.108:8080" + store.put("__host", host) + } + return host + }, + lastID: function (id) { + if (id) { + return store.put('__lastID', id) + } + return store.get('__lastID', 0) + } +} +let fetch = function () { + //指定确定按钮点击时要执行的动作 + const deviceID = device.getAndroidId() + + let lastID = st.lastID() + let path = "{host}/api/devices/{deviceID}/follower" + let url = path.replace("{host}", st.host()).replace("{deviceID}", deviceID) + if (lastID != 0) { + url = url + "?lastID=" + lastID + } + // log("url =", url) + + let resp = http.get(url) + let body = resp.body.json() + if (body == null) { + toastLog("没有新的数据") + return + } + + st.lastID(body.ID) + // log("body =", body) + + return body.UID +} + +module.exports = { + duration: 1000, + global: global, + page: { + user: pageUser, + chat: pageChat, + friend: pageFriend, + }, + messages: messages, + fetch: fetch, + store: st, + kill: function () { + let name = "com.ss.android.ugc.aweme" + app.openAppSetting(name);//通过包名打开应用的详情页(设置页) + text(app.getAppName(name)).waitFor();//通过包名获取已安装的应用名称,判断是否已经跳转至该app的应用设置界面 + sleep(500);//稍微休息一下,不然看不到运行过程,自己用时可以删除这行 + let is_sure = textMatches(/(.*强.*|.*停.*|.*结.*)/).findOne();//在app的应用设置界面找寻包含“强”,“停”,“结”,“行”的控件 + //特别注意,应用设置界面可能存在并非关闭该app的控件,但是包含上述字样的控件,如果某个控件包含名称“行”字 + //textMatches(/(.*强.*|.*停.*|.*结.*|.*行.*)/)改为textMatches(/(.*强.*|.*停.*|.*结.*)/) + //或者结束应用的控件名为“结束运行”直接将textMatches(/(.*强.*|.*停.*|.*结.*|.*行.*)/)改为text("结束运行") + + if (is_sure.enabled()) {//判断控件是否已启用(想要关闭的app是否运行) + is_sure.parent().click();//结束应用的控件如果无法点击,需要在布局中找寻它的父控件,如果还无法点击,再上一级控件,本案例就是控件无法点击 + textMatches(/(.*确.*|.*定.*)/).findOne().click();//需找包含“确”,“定”的控件 + log(app.getAppName(name) + "应用已被关闭"); + sleep(1000); + back(); + } else { + log(app.getAppName(name) + "应用不能被正常关闭或不在后台运行"); + back(); + } + }, +} \ No newline at end of file diff --git a/robot/main.js b/robot/main.js new file mode 100644 index 0000000..8509553 --- /dev/null +++ b/robot/main.js @@ -0,0 +1,29 @@ +let lib = require("./autojs/lib.js") +log(currentActivity()) +// exit() +// 先回主页面 +home() +sleep(1000) + + +// lib.kill() +// sleep(2000) + +lib.page.user.open() +// let uid = lib.fetch() +// log("UID =",uid) +// if (uid) { +// lib.page.user.open(uid) +// // lib.store.addTimes() +// sleep(1000) +// // home() +// } else { +// toastLog("没有找到用户ID") +// } + + +// className("android.widget.TextView").find().forEach(function (tv) { +// if (tv.text() != "") { +// log(tv.text()); +// } +// }) \ No newline at end of file diff --git a/robot/main.js.bak b/robot/main.js.bak new file mode 100644 index 0000000..28706a2 --- /dev/null +++ b/robot/main.js.bak @@ -0,0 +1,81 @@ +"ui"; + +// auto.waitFor(); + +var storage = storages.create("_##_@_DY_Proxy"); + +ui.layout( + + + + + +); + +let times = storage.get('__times', 0) +let host = storage.get("__host", "") +log("HOST=", host) +if (host.length == 0) { + host = "http://10.1.1.108:8080" + storage.put("__host", host) +} + +//指定确定按钮点击时要执行的动作 +const deviceID = device.getAndroidId() +// split device per 4 char with - +let __deviceID = deviceID.slice(0, 4) + "-" + deviceID.slice(4, 8) + "-" + deviceID.slice(8, 12) + "-" + deviceID.slice(12, 16) +log(__deviceID) +ui.id.setText(__deviceID) +ui.id.click(() => { + setClip(__deviceID) + toast("已复制到剪贴板") +}) + +//启用按键监听 +events.setKeyInterceptionEnabled("volume_up", true); +events.setKeyInterceptionEnabled("volume_down", true); + +events.observeKey(); +events.observeNotification(); + +let eventVolumeUp = function (event) { + toast("音量上键被按下了"); + let lastID = storage.get('__lastID', 0) + + let path = "{host}/api/devices/{deviceID}/follower" + let url = path.replace("{host}", host).replace("{deviceID}", deviceID) + if (lastID != 0) { + url = url + "?lastID=" + lastID + } + log("url =", url) + + // let resp = http.get(url) + // let body = resp.body.json() + // if (body == null) { + // toast("没有新的数据") + // return + // } + // storage.put('__lastID', body.ID) + // log("lastID =", storage.get('__lastID', 0)) + // log("body =", body) +} + +let eventHandler = { + volumeUp: eventVolumeUp, + volumeDown: function (event) { + toast("音量下键被按下了"); + exit(); + }, + notification: function (n) { + log("通知时间为:" + new Date(n.when)); + log("应用包名为:" + n.getPackageName()); + log("标题:" + n.getTitle()); + log("内容:" + n.getText()); + } +}; + +events.onKeyUp("volume_up", eventHandler.volumeUp); +events.onKeyDown("volume_down", eventHandler.volumeDown); +events.on("notification", eventHandler.notification); + +// setInterval(() => { log("WORKING...") }, 1000); \ No newline at end of file diff --git a/robot/project.json b/robot/project.json new file mode 100644 index 0000000..3320a32 --- /dev/null +++ b/robot/project.json @@ -0,0 +1,10 @@ +{ + "name": "X", + "main": "main.js", + "ignore": [ + "build" + ], + "packageName": "com.example", + "versionName": "1.0.0", + "versionCode": 1 +} \ No newline at end of file diff --git a/robot/readme.md b/robot/readme.md new file mode 100644 index 0000000..e13bdd4 --- /dev/null +++ b/robot/readme.md @@ -0,0 +1,7 @@ + git config --global user.email "rogeecn@qq.com" + git config --global user.name "Rogee" + +activity: + + com.ss.android.ugc.aweme.profile.ui.UserProfileActivity + diff --git a/robot/test.js b/robot/test.js new file mode 100644 index 0000000..0fb4b29 --- /dev/null +++ b/robot/test.js @@ -0,0 +1,6 @@ +// random char/num 16 length +const id = "1234567890abcdef" +let deviceID = id.toUpperCase() +// cut deviceID per 4 char with -, no regexp +deviceID = deviceID.slice(0, 4) + "-" + deviceID.slice(4, 8) + "-" + deviceID.slice(8, 12) + "-" + deviceID.slice(12, 16) +console.log(deviceID) \ No newline at end of file diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..e69de29