This commit is contained in:
lost+skunk 2024-08-01 22:48:05 +03:00
parent 2dfeaae772
commit c5514c3875
23 changed files with 445 additions and 249 deletions

7
INSTANCES.md Normal file
View File

@ -0,0 +1,7 @@
|Instance|Yggdrasil|I2P|Tor|NSFW|Proxifying|Modified Sources|Country|
|:------:|:-------:|:-:|:-:|:--:|:--------:|:--------------:|:-----:|
|[skunky.ebloid.ru](https://skunky.ebloid.ru/art)|[Yes](http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art)|No|No| No | No | No | Russia |
|[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | No | Sweden |
|[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | No | Romania |
|[frontendfriendly.xyz](https://skunkyart.frontendfriendly.xyz)|No|No|No| Yes | Yes | No | Finland |
|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | US |

View File

@ -1,83 +1,33 @@
<img src="https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/misc/logo.png" alt="SkunkyArt" title="SkunkyArt Logo" width="20%"/>
<img src="https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/misc/logo.png" alt="SkunkyArt" title="SkunkyArt Logo" width="20%" loading="lazy"/>
[![Matrix room](https://img.shields.io/badge/matrix-000000?style=for-the-badge&logo=Matrix&logoColor=white)](https://go.kde.org/matrix/#/#skunkyart:ebloid.ru)
# Instances
|Instance|Yggdrasil|I2P|Tor|NSFW|Proxifying|Country|
|:------:|:-------:|:-:|:-:|:--:|:--------:|:-----:|
|[skunky.ebloid.ru](https://skunky.ebloid.ru/art)|[Yes](http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art)|No|No| No | No | Russia |
|[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | Sweden |
|[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | Romania |
|[frontendfriendly.xyz](https://skunkyart.frontendfriendly.xyz)|No|No|No| Yes | Yes | Finland |
|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | US |
Instances: [`INSTANCES.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/INSTANCES.md)
# EN 🇺🇸
## Description
SkunkyArt 🦨 -- alternative frontend to DeviantArt, which will work without problems even on quite old hardware, due to the lack of JavaScript.
## Config
The sample config is in the `config.example.json` file. To specify your own path to the config, use the CLI argument `-c` or `--config`.
* `listen` -- the address and port on which SkunkyArt will listen
* `base-path` -- the path to the instance. Example: `"base-path":"/art/"` -> https://skunky.ebloid.ru/art/
* `cache` -- caching system; default is off.
* `path` -- the path to the cache
* `lifetime` -- the lifetime of the file in the cache. Units: i, h, d, w, m, y. I -- minute, all other units I think are self-explanatory.
* `max-size` -- maximum file size in megabytes.
* `dirs-to-memory` -- this setting determines which directories will be copied to RAM when SkunkyArt is started. Mandatory
* `download-proxy` -- proxy address for downloading files.
## Examples of reverse proxies
Nginx:
```apache
server {
listen 443 ssl;
server_name skunky.example.com;
location ((BASE URL)) { # if you have a separate subdomain for the frontend, insert '/' instead of '((BASE URL)))'.
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_pass http://((IP)):((PORT));
}
}
```
Pretty much business as usual, except for the [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) header setting.
## How do I add my instance to the list?
To do this, you must either make a PR by adding your instance to the `instances.json` and `README.md` files, or create an Issue, or report it to the room in Matrix. I don't think it needs any description. However, be warned, this list has a couple rules:
SkunkyArt 🦨 — alternative frontend for DevianArt, which works without JS.
## Setup
The sample config is in the `config.example.json` file. For custom config, use `--config` option.
See the [`SETUP.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP.md) file for more info about directives.
## Adding instance to the list
To do this, you must either make a PR by adding your instance to the `instances.json` and `INSTANCES.md` files (you can use `--add-instance` cli-argument to automatically add the instance to these files), or create an Issue, or report it to the room in Matrix. Keep in mind that your instance must comply with the following rules:
1. the Instance must not use Cloudflare.
2. If your instance has modified source code, you need to publish it to any free platform. For example, Github and Gitlab are not.
## Acknowledgements
* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) -- helped me understand Go and gave me a lot of useful advice on this language.
* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — helped me understand Go and gave me a lot of useful advice on this language.
* [meoww](https://codeberg.org/meoww) — translated some sentences into English and wrote a service for openrc
# RU 🇷🇺
## Описание
SkunkyArt 🦨 -- альтернативный фронтенд к DeviantArt, который будет работать без проблем даже на довольно старом оборудовании, за счёт отсутствия JavaScript.
## Конфиг
Пример конфига находится в файле `config.example.json`. Чтобы указать свой путь до конфига, используйте CLI-аргумент `-c` или `--config`.
* `listen` -- адрес и порт, на котором будет слушать SkunkyArt
* `base-path` -- путь к инстансу. Пример: `"base-path": "/art/"` -> https://skunky.ebloid.ru/art/
* `cache` -- система кеширования; по умолчанию - выкл.
* `path` -- путь до кеша
* `lifetime` -- время жизни файла в кеше. Единицы измерения: i, h, d, w, m, y. I -- минута, всё остальные единицы измерения, я считаю понятными и без объяснения.
* `max-size` -- максимальный размер файла в мегабайтах
* `dirs-to-memory` -- данная настройка определяет какие каталоги будут скопированы в ОЗУ при запуске SkunkyArt. Обязательна
* `download-proxy` -- адрес прокси для загрузки файлов
## Примеры reverse-прокси
Nginx:
```apache
server {
listen 443 ssl;
server_name skunky.example.com;
location ((BASE URL)) { # если у вас отдельный поддомен для фронтенда, вместо '((BASE URL))' вставляйте '/'
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_pass http://((IP)):((PORT));
}
}
```
В целом, всё как обычно, за исключением настройки заголовка [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto).
## Как добавить свой инстанс в список?
Чтобы это сделать, вы должны либо сделать PR, добавив в файлы `instances.json` и `README.md` свой инстанс, либо создать Issue, или сообщить о нём в комнате в Matrix. Думаю, он не нуждается в описании. Однако учтите, у этого списка есть пара правил:
1. Инстанс не должен использовать Cloudflare.
SkunkyArt 🦨 — альтернативный фронтенд к DeviantArt, который полностью работает без JS (JavaScript).
## Настройка
Пример конфига находится в файле `config.example.json`. Чтобы указать свой конфиг, используйте cli-аргумент `--config`.
См. [`SETUP-RU.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP-RU.md) для информации о настройки фронтенда.
## Добавление инстанса в список
Чтобы это сделать, вы должны либо сделать PR, добавив в файлы `instances.json` и `INSTANCES.md` свой инстанс (можете воспользоваться cli-аргументом `--add-instance`, который автоматически это сделает), либо создать Issue, или сообщить о нём в комнате в Matrix. Учтите, что ваш инстанс должен соблюсти следущие правила:
1. Инстанс не должен использовать Cloudflare итп.
2. Если ваш инстанс имеет модифицированный исходный код, то вам нужно опубликовать его на любую свободную площадку. Например, Github и Gitlab таковыми не являются.
## Благодарности
* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) -- помог разобраться в Go и много чего полезного посоветовал по этому языку.
* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — помог разобраться в Go и много чего полезного посоветовал по этому языку.
* [meoww](https://codeberg.org/meoww) — перевела некоторые предложения на английский язык и написала сервис для openrc

42
SETUP-RU.md Normal file
View File

@ -0,0 +1,42 @@
[English version 🇬🇧](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP.md)
# Единицы измерения
Размер файла в кеше измеряется в мегабайтах.<br>
Единицы времени:
* `i` — минуты
* `h` — часы
* `w` — недели
* `m` — месяца
* `y` — года
# Конфигурация
* `listen` — IP и порт для слушанья; заполняется по такой форме: ip:port
* `uri` — URI инстанса. Пример: `"uri":"/art/"` -> https://skunky.ebloid.ru/art/
* `cache` — Система кеширования; по умолчанию выключена.
* `enabled` — Состояние системы кеширования; требуется булёвое значение
* `path` — Полный путь до каталога, куда будет сохраняться кеш
* `lifetime` — Время жизни файла в кеше, требует целочисленное значение, дополненное суффиксом времени (см. 'Единицы времени')
* `max-size` — Максимальный размер файла
* `update-interval` — Интервал для автоматической ротации кеша
* `dirs-to-memory` — Массив, заполнив который скопируются все файлы из указанных каталогов
* `download-proxy` — Адрес прокси для загрузки файлов
* `user-agent` — Строка, которая используется в качестве User-Agent'а
# Настройка обратного прокси
Если вы собираетесь хостить инстанс в Интернете, то вам следует настроить заголовок прокси [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto). В противном случае, все ссылки на вашем инстансе будут вида "http". Ниже есть информация о том, как настроить обратное проксирование:
Nginx:
```apache
server {
listen 443 ssl;
server_name skunky.example.com;
# Если используется поддомен, то вместо ((BASE_URL)), укажите '/'.
location ((BASE_URL)) {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_pass http://((IP)):((PORT));
}
}
```

42
SETUP.md Normal file
View File

@ -0,0 +1,42 @@
[Версия на русском языке 🇷🇺](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP-RU.md)
# Units
Maximum file size in megabytes, requires numeric value.<br>
Time units:
* `i` — minutes
* `h` — hours
* `w` — weeks
* `m` — months
* `y` — years
# Config
* `listen` — IP and port to listen on in the following form: ip:port
* `uri` — Instance URI. Example: `"uri":"/art/"` -> https://skunky.ebloid.ru/art/
* `cache` — Caching system; default is off.
* `enabled` — Caching system state, requires boolean value
* `path` — Path to cache directory, requires absolute filesystem path
* `lifetime` — Cached file life time, requires numeric value, followed by multiplicative suffix (see Time Units for details)
* `max-size` — Maximum file size in megabytes
* `update-interval` — Automatic rotation interval
* `dirs-to-memory` — This setting determines which directories will be copied to RAM when SkunkyArt is started. Mandatory
* `download-proxy` — Proxy address for downloading files.
* `user-agent` — String, which SkunkyArt uses as UA
# Setting up reverse proxy
Pretty much business as usual, except for the [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) header setting.
Nginx example configuration:
```apache
server {
listen 443 ssl;
server_name skunky.example.com;
# In case of subdomain, use / instend of ((BASE_URL))
location ((BASE_URL)) {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_pass http://((IP)):((PORT));
}
}
```

View File

@ -1,14 +1,17 @@
# v1.3.x
* Написать Makefile
* Почистить говнокод
* **Доделать парсинг описания**
* Избавиться от хардкода под Linux
* ~~Реализовать стрипы в ежедневных артах~~
* Сделать нормальное отображение ошибок
* ~~Исправить баг с навигацией по страницам~~
* ~~Сделать единицы в конфиге более понятными~~
* Добавить возможность включить темплейты в бинарник
* ~~Реализовать миниатюры и оптимизировать CSS под маленькие экраны~~
* Написать Makefile и скрипт для автоматического развёртывания инстанса
* **Реализовать отображение контента, отличного от картинок (видео, аудио, etc)**
* Исправить баг с эмоджи, когда некоторые кастомные эмоции могут не отображаться
* Добавить флаг сборки, который позволит собрать бинарник со встроенными темплейтами
* Улучшить систему кеширования: добавить рейтинг для удаления и копирование изображений в ОЗУ
# v1.4
* Реализовать API

166
app/cli.go Normal file
View File

@ -0,0 +1,166 @@
package app
import (
"bufio"
"bytes"
"encoding/json"
"os"
"time"
)
func ExecuteCommandLineArguments() {
const helpmsg = `SkunkyArt v1.3.1 [CSS improvements for mobile and the strips on Daily Deviations]
Usage:
- [-c|--config] | path to config
- [-a|--add-instance] | generates 'instances.json' and 'INSTANCES.md' files with ur instance
- [-h|--help] | returns this message
Example:
./skunkyart -c config.json
Copyright lost+skunk, X11. https://git.macaw.me/skunky/skunkyart/src/tag/v1.3.1`
a := os.Args[1:]
for n, x := range a {
switch x {
case "-c", "--config":
if len(a) >= 2 {
CFG.cfg = a[n+1]
} else {
exit("Not enought arguments", 1)
}
case "-h", "--help":
exit(helpmsg, 0)
case "-a", "--add-instance":
addInstance()
}
}
}
type settingsUrls struct {
I2P string `json:"i2p,omitempty"`
Ygg string `json:"ygg,omitempty"`
Tor string `json:"tor,omitempty"`
Clearnet string `json:"clearnet,omitempty"`
}
type settingsParams struct {
Nsfw bool `json:"nsfw"`
Proxy bool `json:"proxy"`
}
type settings struct {
Title string `json:"title"`
Country string `json:"country"`
ModifiedSrc string `json:"modified-src,omitempty"`
Urls settingsUrls `json:"urls"`
Settings settingsParams `json:"settings"`
}
func addInstance() {
prompt := func(txt string, necessary bool) string {
input := bufio.NewScanner(os.Stdin)
for {
print(txt)
print(": ")
input.Scan()
if i := input.Text(); necessary && i == "" {
println("Please specify the", txt)
} else {
return i
}
}
}
var settingsVar struct {
Instances []settings `json:"instances"`
}
instancesJson, err := os.OpenFile("instances.test.json", os.O_CREATE|os.O_WRONLY, 0644)
try(err)
defer instancesJson.Close()
instances, err := os.OpenFile("INSTANCES.md", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
try(err)
defer instances.Close()
for {
if Templates["instances.json"] == "" {
print("\rDownloading instance list...")
} else {
println("\r\033[2KDownloaded!")
try(json.Unmarshal([]byte(Templates["instances.json"]), &settingsVar))
settingsVar.Instances = append(settingsVar.Instances, settings{
Title: prompt("Title", true),
Country: prompt("Country", true),
ModifiedSrc: prompt("Link to modified sources", false),
Settings: settingsParams{
Nsfw: CFG.Nsfw,
Proxy: CFG.Proxy,
},
Urls: settingsUrls{
Clearnet: prompt("Clearnet link", false),
Ygg: prompt("Yggdrasil link", false),
Tor: prompt("Onion link", false),
I2P: prompt("I2P link", false),
},
})
j, err := json.MarshalIndent(&settingsVar, "", " ")
try(err)
instancesJson.Write(j)
settingsVar := &settingsVar.Instances[len(settingsVar.Instances)-1]
var mdstr bytes.Buffer
mdstr.WriteString("\n|")
if settingsVar.Urls.Clearnet != "" {
mdstr.WriteString("[")
mdstr.WriteString(settingsVar.Title)
mdstr.WriteString("](")
mdstr.WriteString(settingsVar.Urls.Clearnet)
mdstr.WriteString(")")
} else {
mdstr.WriteString(settingsVar.Title)
}
mdstr.WriteString("|")
urls := []string{settingsVar.Urls.Ygg, settingsVar.Urls.I2P, settingsVar.Urls.Tor}
for i, l := 0, len(urls); i < l; i++ {
url := urls[i]
if url != "" {
mdstr.WriteString("[Yes](")
mdstr.WriteString(url)
mdstr.WriteString(")|")
} else {
mdstr.WriteString("No|")
}
}
settings := []bool{settingsVar.Settings.Nsfw, settingsVar.Settings.Proxy}
for i, l := 0, len(settings); i < l; i++ {
if settings[i] {
mdstr.WriteString("Yes|")
} else {
mdstr.WriteString("No|")
}
}
if settingsVar.ModifiedSrc != "" {
mdstr.WriteString("[Yes](")
mdstr.WriteString(settingsVar.ModifiedSrc)
mdstr.WriteString(")|")
} else {
mdstr.WriteString("No|")
}
mdstr.WriteString(settingsVar.Country)
mdstr.WriteString("|")
instances.Write(mdstr.Bytes())
break
}
time.Sleep(500 * time.Millisecond)
}
exit("Done! Now add the files 'instances.json' and 'INSTANCES.md' to the 'master' branch in the repository https://git.macaw.me/skunky/SkunkyArt", 0)
}

View File

@ -21,7 +21,7 @@ type cache_config struct {
type config struct {
cfg string
Listen string
BasePath string `json:"base-path"`
URI string `json:"uri"`
Cache cache_config
Proxy, Nsfw bool
UserAgent string `json:"user-agent"`
@ -30,15 +30,15 @@ type config struct {
}
var CFG = config{
cfg: "config.json",
Listen: "127.0.0.1:3003",
BasePath: "/",
cfg: "config.json",
Listen: "127.0.0.1:3003",
URI: "/",
Cache: cache_config{
Enabled: true,
Enabled: false,
Path: "cache",
UpdateInterval: 1,
},
Dirs: []string{"html", "css"},
Dirs: []string{"html", "css", "misc"},
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
Proxy: true,
Nsfw: true,
@ -47,42 +47,6 @@ var CFG = config{
var lifetimeParsed int64
func ExecuteConfig() {
go func() {
for {
func() {
defer func() {
if r := recover(); r != nil {
recover()
}
}()
Templates["instances.json"] = string(Download("https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/instances.json").Body)
}()
time.Sleep(1 * time.Hour)
}
}()
const helpmsg = `SkunkyArt v1.3.1 [CSS improvements for mobile and strips on Daily Deviations]
Usage:
- [-c|--config] - path to config
- [-h|--help] - returns this message
Example:
./skunkyart -c config.json
Copyright lost+skunk, X11. https://git.macaw.me/skunky/skunkyart/src/tag/v1.3.1`
a := os.Args
for n, x := range a {
switch x {
case "-c", "--config":
if len(a) >= 3 {
CFG.cfg = a[n+1]
} else {
exit("Not enought arguments", 1)
}
case "-h", "--help":
exit(helpmsg, 0)
}
}
if CFG.cfg != "" {
f, err := os.ReadFile(CFG.cfg)
tryWithExitStatus(err, 1)

View File

@ -11,7 +11,7 @@ var Host string
func Router() {
parsepath := func(path string) map[int]string {
if l := len(CFG.BasePath); len(path) > l {
if l := len(CFG.URI); len(path) > l {
path = path[l-1:]
} else {
path = "/"
@ -54,7 +54,7 @@ func Router() {
var skunky skunkyart
skunky.Writer = w
skunky.Args = r.URL.Query()
skunky.BasePath = CFG.BasePath
skunky.BasePath = CFG.URI
arg := skunky.Args.Get
skunky.QueryRaw = arg("q")
@ -75,7 +75,7 @@ func Router() {
default:
skunky.ReturnHTTPError(404)
case "":
skunky.ExecuteTemplate("index.htm", &CFG.BasePath)
skunky.ExecuteTemplate("index.htm", &CFG.URI)
case "post":
skunky.Deviation(path[2], path[3])
case "search":

View File

@ -32,6 +32,20 @@ func tryWithExitStatus(err error, code int) {
}
}
func RefreshInstances() {
for {
func() {
defer func() {
if r := recover(); r != nil {
recover()
}
}()
Templates["instances.json"] = string(Download("https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/instances.json").Body)
}()
time.Sleep(1 * time.Hour)
}
}
// some crap for frontend
func (s skunkyart) ExecuteTemplate(file string, data any) {
var buf strings.Builder
@ -46,7 +60,7 @@ func UrlBuilder(strs ...string) string {
var str strings.Builder
l := len(strs)
str.WriteString(Host)
str.WriteString(CFG.BasePath)
str.WriteString(CFG.URI)
for n, x := range strs {
str.WriteString(x)
if n+1 < l && !(strs[n+1][0] == '?' || strs[n+1][0] == '&') && !(x[0] == '?' || x[0] == '&') {
@ -220,11 +234,10 @@ func BuildUserPlate(name string) string {
func GetValueOfTag(t *html.Tokenizer) string {
for tt := t.Next(); ; {
switch tt {
default:
return ""
case html.TextToken:
if tt == html.TextToken {
return string(t.Text())
} else {
return ""
}
}
}

View File

@ -31,20 +31,7 @@ type skunkyart struct {
About struct {
Proxy bool
Nsfw bool
Instances []struct {
Title string
Country string
Urls []struct {
I2P string `json:"i2p"`
Ygg string
Tor string
Clearnet string
}
Settings struct {
Nsfw bool
Proxy bool
}
}
Instances []settings
}
SomeList string
@ -229,48 +216,49 @@ func (s skunkyart) GRUser() {
// посты
func (s skunkyart) Deviation(author, postname string) {
id_search := regexp.MustCompile("[0-9]+").FindAllString(postname, -1)
if len(id_search) >= 1 {
post := &s.Templates.Deviation
id := id_search[len(id_search)-1]
post.Post = devianter.GetDeviation(id, author)
if post.Post.Deviation.TextContent.Excerpt != "" {
post.Post.Description = ParseDescription(post.Post.Deviation.TextContent)
} else {
post.Post.Description = ParseDescription(post.Post.Deviation.Extended.DescriptionText)
}
// время публикации
post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
post.Post.IMG = ParseMedia(post.Post.Deviation.Media)
for _, x := range post.Post.Deviation.Extended.RelatedContent {
if len(x.Deviations) != 0 {
post.Related += s.DeviationList(x.Deviations, false)
}
}
// хештэги
for _, x := range post.Post.Deviation.Extended.Tags {
var tag strings.Builder
tag.WriteString(` <a href="`)
tag.WriteString(UrlBuilder("search", "?q=", x.Name, "&type=tag"))
tag.WriteString(`">#`)
tag.WriteString(x.Name)
tag.WriteString("</a>")
post.Tags += tag.String()
}
if post.Post.Comments.Total <= 50 {
post.Post.Comments.Cursor = ""
}
post.Comments = s.ParseComments(devianter.GetComments(id, post.Post.Comments.Cursor, s.Page, 1))
s.ExecuteTemplate("deviantion.htm", &s)
} else {
if len(id_search) < 1 {
s.ReturnHTTPError(400)
return
}
post := &s.Templates.Deviation
id := id_search[len(id_search)-1]
post.Post = devianter.GetDeviation(id, author)
if post.Post.Deviation.TextContent.Excerpt != "" {
post.Post.Description = ParseDescription(post.Post.Deviation.TextContent)
} else {
post.Post.Description = ParseDescription(post.Post.Deviation.Extended.DescriptionText)
}
// время публикации
post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
post.Post.IMG = ParseMedia(post.Post.Deviation.Media)
for _, x := range post.Post.Deviation.Extended.RelatedContent {
if len(x.Deviations) != 0 {
post.Related += s.DeviationList(x.Deviations, false)
}
}
// хештэги
for _, x := range post.Post.Deviation.Extended.Tags {
var tag strings.Builder
tag.WriteString(` <a href="`)
tag.WriteString(UrlBuilder("search", "?q=", x.Name, "&type=tag"))
tag.WriteString(`">#`)
tag.WriteString(x.Name)
tag.WriteString("</a>")
post.Tags += tag.String()
}
if post.Post.Comments.Total <= 50 {
post.Post.Comments.Cursor = ""
}
post.Comments = s.ParseComments(devianter.GetComments(id, post.Post.Comments.Cursor, s.Page, 1))
s.ExecuteTemplate("deviantion.htm", &s)
}
func (s skunkyart) DD() {
@ -298,7 +286,6 @@ func (s skunkyart) DD() {
}
func (s skunkyart) Search() {
s.Atom = false
var err error
ss := &s.Templates.Search
switch s.Type {
@ -365,15 +352,16 @@ func (s skunkyart) Search() {
}
func (s skunkyart) Emojitar(name string) {
if name != "" && (s.Type == 'a' || s.Type == 'e') {
ae, e := devianter.AEmedia(name, s.Type)
if e != nil {
s.ReturnHTTPError(404)
}
wr(s.Writer, ae)
} else {
if name == "" || !(s.Type == 'a' || s.Type == 'e') {
s.ReturnHTTPError(400)
return
}
ae, e := devianter.AEmedia(name, s.Type)
if e != nil {
s.ReturnHTTPError(404)
}
wr(s.Writer, ae)
}
func (s skunkyart) About() {

View File

@ -1,17 +1,17 @@
{
"listen": "0.0.0.0:3003",
"base-path": "/",
"listen": "0:3003",
"uri": "/",
"cache": {
"enabled": true,
"path": "/home/skunk/projects/skunkyart/cache",
"lifetime": "1w",
"path": "cache",
"lifetime": null,
"max-size": 1024,
"update-interval": 5
},
"dirs-to-memory": [
"/home/skunk/projects/skunkyart/html",
"/home/skunk/projects/skunkyart/css",
"/home/skunk/projects/skunkyart/misc"
"html",
"css",
"misc"
],
"download-proxy": "http://127.0.0.1:8080",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",

4
go.mod
View File

@ -2,9 +2,7 @@ module skunkyart
go 1.22.3
replace git.macaw.me/skunky/devianter v0.2.0 => /home/skunk/projects/devianter
require (
git.macaw.me/skunky/devianter v0.2.0
git.macaw.me/skunky/devianter v0.2.5
golang.org/x/net v0.27.0
)

4
go.sum
View File

@ -1,4 +1,4 @@
git.macaw.me/skunky/devianter v0.2.0 h1:2vnMPb1Dax37CbAOfmHcSoK8+1goFkWHbtbh31Ytsww=
git.macaw.me/skunky/devianter v0.2.0/go.mod h1:ZLn527xBlnpXrUB1B8z/MhyeiWVK4nPWjyfnhWOE8Is=
git.macaw.me/skunky/devianter v0.2.5 h1:aAc6CG/ghvG130Ob7gGUdK4IV3MSeCD5t3QIJjto1M0=
git.macaw.me/skunky/devianter v0.2.5/go.mod h1:ZLn527xBlnpXrUB1B8z/MhyeiWVK4nPWjyfnhWOE8Is=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=

View File

@ -3,6 +3,7 @@
<head>
<title>SkunkyArt</title>
<link rel="stylesheet" href="{{.BasePath}}stylesheet">
<link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
</head>
<main>
<header>
@ -21,43 +22,43 @@
SkunkyArt is an alternative frontend for deviantart.com, written in Go.
</p>
<h3><a href="https://go.kde.org/matrix/#/#skunkyart:ebloid.ru" target="_blank">Room in Matrix</a></h3>
<h2>Instance settings:</h2>
<b>Instance settings:</b>
<ul>
<li><b>NSFW</b>: <span class="about-{{.Templates.About.Nsfw}}">{{if .Templates.About.Nsfw}}YES{{else}}NO{{end}}</span></li>
<li><b>Proxyfing</b>: <span class="about-{{.Templates.About.Proxy}}">{{if .Templates.About.Proxy}}YES{{else}}NO{{end}}</span></li>
</ul>
<h2>Instances:</h2>
<ul>
{{range .Templates.About.Instances}}
<li><u><b>{{.Title}}</b></u>:
<ul>
<li><b>Country</b>: {{.Country}}</li>
<li><b>URLs</b>: </li>
<details>
<summary><b>Instances:</b></summary>
<ul>
{{range .Templates.About.Instances}}
<li><u><b>{{.Title}}</b></u>:
<ul>
{{range .Urls}}
{{if ne .I2P ""}}
<li><b>I2P</b>: <a href="{{.I2P}}">Yes</a></li>
{{end}}
{{if ne .Ygg ""}}
<li><b>Ygg</b>: <a href="{{.Ygg}}">Yes</a></li>
{{end}}
{{if ne .Tor ""}}
<li><b>Tor</b>: <a href="{{.Tor}}">Yes</a></li>
{{end}}
{{if ne .Clearnet ""}}
<li><b>Clearnet</b>: <a href="{{.Clearnet}}">{{.Clearnet}}</a></li>
{{end}}
{{end}}
<li><b>Country</b>: {{.Country}}</li>
<li><b>URLs</b>: </li>
<ul>
{{if ne .Urls.I2P ""}}
<li><b>I2P</b>: <a href="{{.Urls.I2P}}">Yes</a></li>
{{end}}
{{if ne .Urls.Ygg ""}}
<li><b>Ygg</b>: <a href="{{.Urls.Ygg}}">Yes</a></li>
{{end}}
{{if ne .Urls.Tor ""}}
<li><b>Tor</b>: <a href="{{.Urls.Tor}}">Yes</a></li>
{{end}}
{{if ne .Urls.Clearnet ""}}
<li><b>Clearnet</b>: <a href="{{.Urls.Clearnet}}">{{.Urls.Clearnet}}</a></li>
{{end}}
</ul>
<li><b>Settings</b>: </li>
<ul>
<li><b>NSFW</b>: <span class="about-{{.Settings.Nsfw}}">{{if .Settings.Nsfw}}YES{{else}}NO{{end}}</span></li>
<li><b>Proxyfing</b>: <span class="about-{{.Settings.Proxy}}">{{if .Settings.Proxy}}YES{{else}}NO{{end}}</span></li>
</ul>
</ul>
<li><b>Settings</b>: </li>
<ul>
<li><b>NSFW</b>: <span class="about-{{.Settings.Nsfw}}">{{if .Settings.Nsfw}}YES{{else}}NO{{end}}</span></li>
<li><b>Proxyfing</b>: <span class="about-{{.Settings.Proxy}}">{{if .Settings.Proxy}}YES{{else}}NO{{end}}</span></li>
</ul>
</ul>
</li>
{{end}}
</ul>
</li>
{{end}}
</ul>
</details>
<p>Copyright <a href="https://go.kde.org/matrix/#/@softpigeones:ebloid.ru" target="_blank">lost+skunk</a>, X11. <a href="https://git.macaw.me/skunky/skunkyart/src/tag/v1.3.1" target="_blank">SkunkyArt v1.3.1</a></p>
</main>
</html>

View File

@ -3,6 +3,7 @@
<head>
<title>SkunkyArt | Daily Deviations</title>
<link rel="stylesheet" href="{{.BasePath}}stylesheet">
<link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
</head>
<main>
<header>

View File

@ -4,6 +4,7 @@
<title>SkunkyArt | {{.Templates.Deviation.Post.Deviation.Author.Username}} - {{.Templates.Deviation.Post.Deviation.Title}}</title>
<link rel="stylesheet" href="{{.BasePath}}stylesheet">
<meta name="referrer" content="no-referrer" />
<link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
</head>
<main>
<header>

View File

@ -9,6 +9,7 @@
{{end}}
</title>
<link rel="stylesheet" href="{{.BasePath}}stylesheet">
<link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
</head>
<main>
<header>

View File

@ -3,6 +3,7 @@
<head>
<title>SkunkyArt</title>
<link rel="stylesheet" href="{{.}}stylesheet"/>
<link rel="icon" type="image/x-icon" href="{{.}}favicon.ico">
</head>
<main>
<center>

View File

@ -3,6 +3,7 @@
<head>
<title>SkunkyArt | Search "{{.QueryRaw}}"</title>
<link rel="stylesheet" href="{{.BasePath}}stylesheet">
<link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
</head>
<main>
<header>

View File

@ -3,10 +3,10 @@
{
"title": "skunky.ebloid.ru",
"country": "Russia",
"urls": [{
"urls": {
"ygg": "http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art",
"clearnet": "https://skunky.ebloid.ru/art"
}],
},
"settings": {
"nsfw": false,
"proxy": false
@ -15,9 +15,9 @@
{
"title": "clovius.club",
"country": "Sweden",
"urls": [{
"urls": {
"clearnet": "https://skunky.clovius.club"
}],
},
"settings": {
"nsfw": true,
"proxy": true
@ -26,9 +26,9 @@
{
"title": "bloat.cat",
"country": "Romania",
"urls": [{
"urls": {
"clearnet": "https://skunky.bloat.cat"
}],
},
"settings": {
"nsfw": true,
"proxy": true
@ -37,9 +37,9 @@
{
"title": "frontendfriendly.xyz",
"country": "Finland",
"urls": [{
"urls": {
"clearnet": "https://skunkyart.frontendfriendly.xyz"
}],
},
"settings": {
"nsfw": true,
"proxy": true
@ -48,9 +48,9 @@
{
"title": "lumaeris.com",
"country": "US",
"urls": [{
"urls": {
"clearnet": "https://skunkyart.lumaeris.com"
}],
},
"settings": {
"nsfw": true,
"proxy": true

18
main.go
View File

@ -2,17 +2,27 @@ package main
import (
"skunkyart/app"
"time"
"git.macaw.me/skunky/devianter"
)
func main() {
go app.RefreshInstances()
app.ExecuteCommandLineArguments()
app.ExecuteConfig()
app.CopyTemplatesToMemory()
err := devianter.UpdateCSRF()
if err != nil {
println(err.Error())
}
go func() {
for {
err := devianter.UpdateCSRF()
if err != nil {
println(err.Error())
}
time.Sleep(12 * time.Hour)
}
}()
app.Router()
}

View File

@ -1,13 +1,20 @@
#!/sbin/openrc-run
directory=<path_to_dir_with_skunkyart>
# command_args="-c $directory/config.json" # if SA wasn't start, try to uncomment this line
command=$directory/skunkyart
name="SkunkyArt"
description="Privacy frontend for deviantart.com"
supervisor=supervise-daemon
user=skunkyart:skunkyart
name=SkunkyArt
directory=/opt/skunkyart
command=$directory/bin/skunkyart
description="Privacy-oriented frontend for DeviantArt"
error_logger=logger
output_logger=logger
no_new_privs=true
umask=0077
# if you use old version openrc, uncomment function lower
# start_pre() {
# cd $directory
# }
depend() {
need net
need net localmount bootmisc
}

View File

@ -1,7 +1,7 @@
# Note: i didn't use systemd, so it can be not works :)
[Unit]
Description=Privacy frontend for deviantart.com
Description=Privacy-oriented frontend for DeviantArt
[Service]
Directory=<path-to-dir-with-skunkyart>