feat: imoplement basic structure
This commit is contained in:
parent
c312239de8
commit
eb1daac82f
@ -1,5 +1,132 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.anthrove.art/Anthrove/gorse-playground/internal/logic"
|
||||
"git.anthrove.art/Anthrove/gorse-playground/pkg/models"
|
||||
"git.anthrove.art/Anthrove/gorse-playground/pkg/utils"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
router := gin.Default()
|
||||
store := cookie.NewStore([]byte("secret"))
|
||||
router.Use(sessions.Sessions("mysession", store))
|
||||
router.Use(func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
|
||||
userid := session.Get("userid")
|
||||
|
||||
if userid == nil && c.FullPath() != "/login" {
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
})
|
||||
|
||||
router.LoadHTMLGlob("web/template/**/*")
|
||||
|
||||
router.GET("/login", func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "login.gohtml", nil)
|
||||
})
|
||||
router.POST("/login", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userID := c.PostForm("userid")
|
||||
// Handle the user ID as needed
|
||||
session.Set("userid", userID)
|
||||
err := session.Save()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, "/profile")
|
||||
})
|
||||
router.GET("/logout", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
session.Clear()
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
})
|
||||
router.GET("/profile", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userid := session.Get("userid")
|
||||
|
||||
c.HTML(http.StatusOK, "profile.gohtml", gin.H{"userid": userid})
|
||||
})
|
||||
router.POST("/scrape", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userid := session.Get("userid")
|
||||
useridInt, err := strconv.Atoi(userid.(string))
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"convert error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
allFaves, err := logic.GetAllFavorites(c, useridInt)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"get faves error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
items, faves, err := utils.ConvertE6ToGorse(userid.(string), allFaves)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"convert to gorse error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = logic.UpsertUser(c, models.GorseUser{
|
||||
Comment: "",
|
||||
Labels: nil,
|
||||
Subscribe: nil,
|
||||
UserId: userid.(string),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"upsert user error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = logic.UpsertItems(c, items)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"upsert items error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = logic.UpsertFavorites(c, faves)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"upsert faves error": err.Error()})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"items": items})
|
||||
})
|
||||
router.GET("/post", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
userid := session.Get("userid")
|
||||
page := c.DefaultQuery("page", "1")
|
||||
pageInt, err := strconv.Atoi(page)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
recs, err := logic.GetUserFavorites(c, userid.(string), pageInt)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "post.gohtml", gin.H{"recs": recs, "next_page": pageInt + 1, "last_page": pageInt - 1})
|
||||
})
|
||||
|
||||
router.Run(":8080")
|
||||
|
||||
}
|
||||
|
253
config/gorse/config.toml
Normal file
253
config/gorse/config.toml
Normal file
@ -0,0 +1,253 @@
|
||||
[database]
|
||||
|
||||
# The database for caching, support Redis, MySQL, Postgres and MongoDB:
|
||||
# redis://<user>:<password>@<host>:<port>/<db_number>
|
||||
# rediss://<user>:<password>@<host>:<port>/<db_number>
|
||||
# redis+cluster://<user>:<password>@<host1>:<port1>,<host2>:<port2>,...,<hostN>:<portN>
|
||||
# postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full
|
||||
# postgresql://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full
|
||||
# mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
|
||||
# mongodb+srv://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
|
||||
cache_store = "redis://localhost:6379/0"
|
||||
|
||||
# The database for persist data, support MySQL, Postgres, ClickHouse and MongoDB:
|
||||
# mysql://[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
|
||||
# postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full
|
||||
# postgresql://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full
|
||||
# clickhouse://user:password@host[:port]/database?param1=value1&...¶mN=valueN
|
||||
# chhttp://user:password@host[:port]/database?param1=value1&...¶mN=valueN
|
||||
# chhttps://user:password@host[:port]/database?param1=value1&...¶mN=valueN
|
||||
# mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
|
||||
# mongodb+srv://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
|
||||
data_store = "mysql://gorse:gorse_pass@tcp(localhost:3306)/gorse"
|
||||
|
||||
# The naming prefix for tables (collections, keys) in databases. The default value is empty.
|
||||
table_prefix = ""
|
||||
|
||||
# The naming prefix for tables (collections, keys) in cache storage databases. The default value is `table_prefix`.
|
||||
cache_table_prefix = ""
|
||||
|
||||
# The naming prefix for tables (collections, keys) in data storage databases. The default value is `table_prefix`.
|
||||
data_table_prefix = ""
|
||||
|
||||
[master]
|
||||
|
||||
# GRPC port of the master node. The default value is 8086.
|
||||
port = 8086
|
||||
|
||||
# gRPC host of the master node. The default values is "0.0.0.0".
|
||||
host = "0.0.0.0"
|
||||
|
||||
# HTTP port of the master node. The default values is 8088.
|
||||
http_port = 8088
|
||||
|
||||
# HTTP host of the master node. The default values is "0.0.0.0".
|
||||
http_host = "0.0.0.0"
|
||||
|
||||
# AllowedDomains is a list of allowed values for Http Origin.
|
||||
# The list may contain the special wildcard string ".*" ; all is allowed
|
||||
# If empty all are allowed.
|
||||
http_cors_domains = []
|
||||
|
||||
# AllowedMethods is either empty or has a list of http methods names. Checking is case-insensitive.
|
||||
http_cors_methods = []
|
||||
|
||||
# Number of working jobs in the master node. The default value is 1.
|
||||
n_jobs = 12
|
||||
|
||||
# Meta information timeout. The default value is 10s.
|
||||
meta_timeout = "10s"
|
||||
|
||||
# Username for the master node dashboard.
|
||||
dashboard_user_name = ""
|
||||
|
||||
# Password for the master node dashboard.
|
||||
dashboard_password = ""
|
||||
|
||||
# Secret key for admin APIs (SSL required).
|
||||
admin_api_key = ""
|
||||
|
||||
[server]
|
||||
|
||||
# Default number of returned items. The default value is 10.
|
||||
default_n = 10
|
||||
|
||||
# Secret key for RESTful APIs (SSL required).
|
||||
api_key = ""
|
||||
|
||||
# Clock error in the cluster. The default value is 5s.
|
||||
clock_error = "5s"
|
||||
|
||||
# Insert new users while inserting feedback. The default value is true.
|
||||
auto_insert_user = true
|
||||
|
||||
# Insert new items while inserting feedback. The default value is true.
|
||||
auto_insert_item = true
|
||||
|
||||
# Server-side cache expire time. The default value is 10s.
|
||||
cache_expire = "10s"
|
||||
|
||||
[recommend]
|
||||
|
||||
# The cache size for recommended/popular/latest items. The default value is 10.
|
||||
cache_size = 10
|
||||
|
||||
|
||||
# Recommended cache expire time. The default value is 72h.
|
||||
cache_expire = "72h"
|
||||
|
||||
[recommend.data_source]
|
||||
|
||||
# The feedback types for positive events.
|
||||
positive_feedback_types = ["like"]
|
||||
|
||||
# The feedback types for read events.
|
||||
read_feedback_types = []
|
||||
|
||||
# The time-to-live (days) of positive feedback, 0 means disabled. The default value is 0.
|
||||
positive_feedback_ttl = 0
|
||||
|
||||
# The time-to-live (days) of items, 0 means disabled. The default value is 0.
|
||||
item_ttl = 0
|
||||
|
||||
[recommend.popular]
|
||||
|
||||
# The time window of popular items. The default values is 4320h.
|
||||
popular_window = "720h"
|
||||
|
||||
[recommend.user_neighbors]
|
||||
|
||||
# The type of neighbors for users. There are three types:
|
||||
# similar: Neighbors are found by number of common labels.
|
||||
# related: Neighbors are found by number of common liked items.
|
||||
# auto: If a user have labels, neighbors are found by number of common labels.
|
||||
# If this user have no labels, neighbors are found by number of common liked items.
|
||||
# The default value is "auto".
|
||||
neighbor_type = "similar"
|
||||
|
||||
# Enable approximate user neighbor searching using vector index. The default value is true.
|
||||
enable_index = true
|
||||
|
||||
# Minimal recall for approximate user neighbor searching. The default value is 0.8.
|
||||
index_recall = 0.8
|
||||
|
||||
# Maximal number of fit epochs for approximate user neighbor searching vector index. The default value is 3.
|
||||
index_fit_epoch = 3
|
||||
|
||||
[recommend.item_neighbors]
|
||||
|
||||
# The type of neighbors for items. There are three types:
|
||||
# similar: Neighbors are found by number of common labels.
|
||||
# related: Neighbors are found by number of common users.
|
||||
# auto: If a item have labels, neighbors are found by number of common labels.
|
||||
# If this item have no labels, neighbors are found by number of common users.
|
||||
# The default value is "auto".
|
||||
neighbor_type = "auto"
|
||||
|
||||
# Enable approximate item neighbor searching using vector index. The default value is true.
|
||||
enable_index = true
|
||||
|
||||
# Minimal recall for approximate item neighbor searching. The default value is 0.8.
|
||||
index_recall = 0.8
|
||||
|
||||
# Maximal number of fit epochs for approximate item neighbor searching vector index. The default value is 3.
|
||||
index_fit_epoch = 3
|
||||
|
||||
[recommend.collaborative]
|
||||
|
||||
# Enable approximate collaborative filtering recommend using vector index. The default value is true.
|
||||
enable_index = true
|
||||
|
||||
# Minimal recall for approximate collaborative filtering recommend. The default value is 0.9.
|
||||
index_recall = 0.9
|
||||
|
||||
# Maximal number of fit epochs for approximate collaborative filtering recommend vector index. The default value is 3.
|
||||
index_fit_epoch = 3
|
||||
|
||||
# The time period for model fitting. The default value is "60m".
|
||||
model_fit_period = "60m"
|
||||
|
||||
# The time period for model searching. The default value is "360m".
|
||||
model_search_period = "360m"
|
||||
|
||||
# The number of epochs for model searching. The default value is 100.
|
||||
model_search_epoch = 100
|
||||
|
||||
# The number of trials for model searching. The default value is 10.
|
||||
model_search_trials = 10
|
||||
|
||||
# Enable searching models of different sizes, which consume more memory. The default value is false.
|
||||
enable_model_size_search = false
|
||||
|
||||
[recommend.replacement]
|
||||
|
||||
# Replace historical items back to recommendations. The default value is false.
|
||||
enable_replacement = false
|
||||
|
||||
# Decay the weights of replaced items from positive feedbacks. The default value is 0.8.
|
||||
positive_replacement_decay = 0.8
|
||||
|
||||
# Decay the weights of replaced items from read feedbacks. The default value is 0.6.
|
||||
read_replacement_decay = 0.6
|
||||
|
||||
[recommend.offline]
|
||||
|
||||
# The time period to check recommendation for users. The default values is 1m.
|
||||
check_recommend_period = "1m"
|
||||
|
||||
# The time period to refresh recommendation for inactive users. The default values is 120h.
|
||||
refresh_recommend_period = "24h"
|
||||
|
||||
# Enable latest recommendation during offline recommendation. The default value is false.
|
||||
enable_latest_recommend = false
|
||||
|
||||
# Enable popular recommendation during offline recommendation. The default value is false.
|
||||
enable_popular_recommend = false
|
||||
|
||||
# Enable user-based similarity recommendation during offline recommendation. The default value is false.
|
||||
enable_user_based_recommend = true
|
||||
|
||||
# Enable item-based similarity recommendation during offline recommendation. The default value is false.
|
||||
enable_item_based_recommend = true
|
||||
|
||||
# Enable collaborative filtering recommendation during offline recommendation. The default value is true.
|
||||
enable_collaborative_recommend = false
|
||||
|
||||
# Enable click-though rate prediction during offline recommendation. Otherwise, results from multi-way recommendation
|
||||
# would be merged randomly. The default value is false.
|
||||
enable_click_through_prediction = false
|
||||
|
||||
# The explore recommendation method is used to inject popular items or latest items into recommended result:
|
||||
# popular: Recommend popular items to cold-start users.
|
||||
# latest: Recommend latest items to cold-start users.
|
||||
# The default values is { popular = 0.0, latest = 0.0 }.
|
||||
explore_recommend = { popular = 0.0, latest = 0.0 }
|
||||
|
||||
[recommend.online]
|
||||
|
||||
# The fallback recommendation method is used when cached recommendation drained out:
|
||||
# item_based: Recommend similar items to cold-start users.
|
||||
# popular: Recommend popular items to cold-start users.
|
||||
# latest: Recommend latest items to cold-start users.
|
||||
# Recommenders are used in order. The default values is ["latest"].
|
||||
fallback_recommend = ["item_based"]
|
||||
|
||||
# The number of feedback used in fallback item-based similar recommendation. The default values is 10.
|
||||
num_feedback_fallback_item_based = 10
|
||||
|
||||
[tracing]
|
||||
|
||||
# Enable tracing for REST APIs. The default value is false.
|
||||
enable_tracing = false
|
||||
|
||||
# The type of tracing exporters should be one of "jaeger", "zipkin", "otlp" and "otlphttp". The default value is "jaeger".
|
||||
exporter = "jaeger"
|
||||
|
||||
# The endpoint of tracing collector.
|
||||
collector_endpoint = "http://localhost:14268/api/traces"
|
||||
|
||||
# The type of tracing sampler should be one of "always", "never" and "ratio". The default value is "always".
|
||||
sampler = "always"
|
||||
|
||||
# The ratio of ratio based sampler. The default value is 1.
|
||||
ratio = 1
|
86
deployment/docker-compose.yml
Normal file
86
deployment/docker-compose.yml
Normal file
@ -0,0 +1,86 @@
|
||||
services:
|
||||
redis:
|
||||
image: docker.dragonflydb.io/dragonflydb/dragonfly
|
||||
restart: unless-stopped
|
||||
ulimits:
|
||||
memlock: -1
|
||||
ports:
|
||||
- 6379:6379
|
||||
volumes:
|
||||
- redis:/data
|
||||
|
||||
postgres:
|
||||
image: postgres:17
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
POSTGRES_DB: gorse
|
||||
POSTGRES_USER: gorse
|
||||
POSTGRES_PASSWORD: gorse
|
||||
volumes:
|
||||
- pg_data:/var/lib/postgresql/data
|
||||
|
||||
worker:
|
||||
image: zhenghaoz/gorse-worker
|
||||
restart: unless-stopped
|
||||
command: >
|
||||
--master-host master --master-port 8086
|
||||
--http-host 0.0.0.0 --http-port 8089
|
||||
--log-path /var/log/gorse/worker.log
|
||||
--cache-path /var/lib/gorse/worker_cache.data
|
||||
volumes:
|
||||
- gorse_log:/var/log/gorse # Mount log files in volume.
|
||||
- worker_data:/var/lib/gorse # Mount cache files in volume.
|
||||
depends_on:
|
||||
- master
|
||||
|
||||
server:
|
||||
image: zhenghaoz/gorse-server
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8087:8087 # RESTful APIs and Prometheus metrics export port.
|
||||
command: >
|
||||
--master-host master --master-port 8086
|
||||
--http-host 0.0.0.0 --http-port 8087
|
||||
--log-path /var/log/gorse/server.log
|
||||
--cache-path /var/lib/gorse/server_cache.data
|
||||
volumes:
|
||||
- gorse_log:/var/log/gorse # Mount log files in volume.
|
||||
- server_data:/var/lib/gorse # Mount cache files in volume.
|
||||
depends_on:
|
||||
- master
|
||||
|
||||
master:
|
||||
image: zhenghaoz/gorse-master
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8086:8086 # HTTP port
|
||||
- 8088:8088 # gRPC port
|
||||
environment:
|
||||
# Use Redis as cache storage backend.
|
||||
GORSE_CACHE_STORE: redis://redis:6379
|
||||
# Use MySQL as data storage backend.
|
||||
GORSE_DATA_STORE: postgres://gorse:gorse@postgres:5432/gorse?sslmode=disable
|
||||
command: >
|
||||
-c /etc/gorse/config.toml
|
||||
--log-path /var/log/gorse/master.log
|
||||
--cache-path /var/lib/gorse/master_cache.data
|
||||
volumes:
|
||||
# Mount the configuration file.
|
||||
- ../config/gorse/config.toml:/etc/gorse/config.toml
|
||||
# Mount log files in volume.
|
||||
- gorse_log:/var/log/gorse
|
||||
# Mount cache files in volume.
|
||||
- master_data:/var/lib/gorse
|
||||
depends_on:
|
||||
- redis
|
||||
- postgres
|
||||
|
||||
volumes:
|
||||
pg_data:
|
||||
redis:
|
||||
gorse_log:
|
||||
worker_data:
|
||||
server_data:
|
||||
master_data:
|
32
go.mod
32
go.mod
@ -4,7 +4,39 @@ go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/anthrove/openapi-e621-go v1.1.2 // indirect
|
||||
github.com/bytedance/sonic v1.12.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
github.com/caarlos0/env/v11 v11.3.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sessions v1.0.2 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/gin-gonic/gin v1.10.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.2.2 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/validator.v2 v2.0.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
88
go.sum
88
go.sum
@ -1,8 +1,96 @@
|
||||
github.com/anthrove/openapi-e621-go v1.1.2 h1:x+zYGb9zi5QgHvlhtnwhOJtFgC7DONBfgH7Bc2TDw8w=
|
||||
github.com/anthrove/openapi-e621-go v1.1.2/go.mod h1:CET4X8hWfC1O1APRB2pHjbP4FwRyde6t4EaimijJCG0=
|
||||
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
|
||||
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
|
||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
|
||||
github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
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/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA=
|
||||
github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
|
||||
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
|
||||
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
|
||||
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
|
||||
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=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
@ -1,6 +1,6 @@
|
||||
package config
|
||||
|
||||
type E621 struct {
|
||||
ApiKey string `env:"E621_API_KEY"`
|
||||
Username string `env:"E621_USERNAME"`
|
||||
ApiKey string `env:"E621_API_KEY,required"`
|
||||
Username string `env:"E621_USERNAME,required"`
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package config
|
||||
|
||||
type Gorse struct {
|
||||
Endpoint string `env:"GORSE_ENDPOINT"`
|
||||
Endpoint string `env:"GORSE_ENDPOINT,required"`
|
||||
ApiKey string `env:"GORSE_APIKEY"`
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"golang.org/x/time/rate"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
)
|
||||
|
||||
var e621Config config.E621
|
||||
|
@ -9,6 +9,9 @@ import (
|
||||
"github.com/caarlos0/env/v11"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
)
|
||||
|
||||
var gorseConfig config.Gorse
|
||||
@ -25,6 +28,44 @@ func UpsertUser(ctx context.Context, user models.GorseUser) error {
|
||||
return executeRequest(ctx, http.MethodPost, "/api/user", user)
|
||||
}
|
||||
|
||||
func UpsertItems(ctx context.Context, items []models.GorseItem) error {
|
||||
return executeRequest(ctx, http.MethodPost, "/api/items", items)
|
||||
}
|
||||
|
||||
func UpsertFavorites(ctx context.Context, items []models.GorseFavorite) error {
|
||||
return executeRequest(ctx, http.MethodPost, "/api/feedback", items)
|
||||
}
|
||||
|
||||
func GetUserFavorites(ctx context.Context, userid string, page int) ([]string, error) {
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", gorseConfig.Endpoint+"/api/recommend/"+userid, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Set("name", "50")
|
||||
q.Set("offset", strconv.Itoa((page-1)*50))
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-API-Key", gorseConfig.ApiKey)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
var items []string
|
||||
err = json.NewDecoder(resp.Body).Decode(&items)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func executeRequest(ctx context.Context, method, url string, data any) error {
|
||||
jsonData, err := json.Marshal(data)
|
||||
|
||||
|
41
pkg/utils/convert.go
Normal file
41
pkg/utils/convert.go
Normal file
@ -0,0 +1,41 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"git.anthrove.art/Anthrove/gorse-playground/pkg/models"
|
||||
"github.com/anthrove/openapi-e621-go"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func ConvertE6ToGorse(userId string, posts []openapi.Post) ([]models.GorseItem, []models.GorseFavorite, error) {
|
||||
faves := make([]models.GorseFavorite, 0)
|
||||
items := make([]models.GorseItem, 0)
|
||||
|
||||
for _, post := range posts {
|
||||
tags := make([]string, 0)
|
||||
tags = append(tags, post.Tags.Artist...)
|
||||
tags = append(tags, post.Tags.Character...)
|
||||
tags = append(tags, post.Tags.Contributor...)
|
||||
tags = append(tags, post.Tags.Copyright...)
|
||||
tags = append(tags, post.Tags.General...)
|
||||
tags = append(tags, post.Tags.Invalid...)
|
||||
tags = append(tags, post.Tags.Lore...)
|
||||
tags = append(tags, post.Tags.Meta...)
|
||||
tags = append(tags, post.Tags.Species...)
|
||||
|
||||
items = append(items, models.GorseItem{
|
||||
Comment: post.Description,
|
||||
IsHidden: post.Flags.Deleted,
|
||||
ItemId: strconv.Itoa(int(post.Id)),
|
||||
Labels: tags,
|
||||
Timestamp: post.CreatedAt.String(),
|
||||
})
|
||||
faves = append(faves, models.GorseFavorite{
|
||||
Comment: post.Description,
|
||||
FeedbackType: "like",
|
||||
ItemId: strconv.Itoa(int(post.Id)),
|
||||
UserId: userId,
|
||||
})
|
||||
}
|
||||
|
||||
return items, faves, nil
|
||||
}
|
12
web/template/page/login.gohtml
Normal file
12
web/template/page/login.gohtml
Normal file
@ -0,0 +1,12 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Login Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<form method='post' action='/login'>
|
||||
<label for="userid">User-ID:</label>
|
||||
<input type="text" id="userid" name="userid">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
21
web/template/page/post.gohtml
Normal file
21
web/template/page/post.gohtml
Normal file
@ -0,0 +1,21 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Recommendations Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Available Recommondations</h1>
|
||||
<div>
|
||||
{{range .recs}}
|
||||
<a href="https://e621.net/posts/{{.}}" target="_blank">{{.}}</a><br>
|
||||
{{end}}
|
||||
<form method="get">
|
||||
<input value="{{ .last_page }}" style="display: none" name="page">
|
||||
<button type="submit">Last Page</button>
|
||||
</form>
|
||||
<form method="get">
|
||||
<input value="{{ .next_page }}" style="display: none" name="page">
|
||||
<button type="submit">Next Page</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
11
web/template/page/profile.gohtml
Normal file
11
web/template/page/profile.gohtml
Normal file
@ -0,0 +1,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Profile Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>User {{ .userid }}</h1>
|
||||
<form method="POST" action="/scrape">
|
||||
<button type="submit">Scrape</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user