Commit c5514c3875

c5514c3875b9b782688047d547dfd6663a5b77bb

parent: 2dfeaae772

Unsigned

lost+skunk <skunky@ebloid.ru> · 2024-08-01 19:48 UTC

v1.3

Layout: unified · split

INSTANCES.md added +7
@@ -0,0 +1,7 @@
1|Instance|Yggdrasil|I2P|Tor|NSFW|Proxifying|Modified Sources|Country|
2|:------:|:-------:|:-:|:-:|:--:|:--------:|:--------------:|:-----:|
3|[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 |
4|[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | No | Sweden |
5|[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | No | Romania |
6|[frontendfriendly.xyz](https://skunkyart.frontendfriendly.xyz)|No|No|No| Yes | Yes | No | Finland |
7|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | No | US |\ No newline at end of file
README.md +20 −70
@@ -1,83 +1,33 @@
1<img src="https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/misc/logo.png" alt="SkunkyArt" title="SkunkyArt Logo" width="20%"/> 1<img src="https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/misc/logo.png" alt="SkunkyArt" title="SkunkyArt Logo" width="20%" loading="lazy"/>
2 2
3[![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) 3[![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)
4# Instances 4
5|Instance|Yggdrasil|I2P|Tor|NSFW|Proxifying|Country| 5Instances: [`INSTANCES.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/INSTANCES.md)
6|:------:|:-------:|:-:|:-:|:--:|:--------:|:-----:|
7|[skunky.ebloid.ru](https://skunky.ebloid.ru/art)|[Yes](http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art)|No|No| No | No | Russia |
8|[clovius.club](https://skunky.clovius.club)|No|No|No| Yes | Yes | Sweden |
9|[bloat.cat](https://skunky.bloat.cat)|No|No|No| Yes | Yes | Romania |
10|[frontendfriendly.xyz](https://skunkyart.frontendfriendly.xyz)|No|No|No| Yes | Yes | Finland |
11|[lumaeris.com](https://skunkyart.lumaeris.com)|No|No|No| Yes | Yes | US |
12 6
13# EN 🇺🇸 7# EN 🇺🇸
14## Description 8## Description
15SkunkyArt 🦨 -- alternative frontend to DeviantArt, which will work without problems even on quite old hardware, due to the lack of JavaScript. 9SkunkyArt 🦨 — alternative frontend for DevianArt, which works without JS.
16## Config 10## Setup
17The sample config is in the `config.example.json` file. To specify your own path to the config, use the CLI argument `-c` or `--config`. 11The sample config is in the `config.example.json` file. For custom config, use `--config` option.
18* `listen` -- the address and port on which SkunkyArt will listen 12See the [`SETUP.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP.md) file for more info about directives.
19* `base-path` -- the path to the instance. Example: `"base-path":"/art/"` -> https://skunky.ebloid.ru/art/ 13## Adding instance to the list
20* `cache` -- caching system; default is off. 14To 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:
21 * `path` -- the path to the cache
22 * `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.
23 * `max-size` -- maximum file size in megabytes.
24* `dirs-to-memory` -- this setting determines which directories will be copied to RAM when SkunkyArt is started. Mandatory
25* `download-proxy` -- proxy address for downloading files.
26## Examples of reverse proxies
27Nginx:
28```apache
29server {
30 listen 443 ssl;
31 server_name skunky.example.com;
32
33 location ((BASE URL)) { # if you have a separate subdomain for the frontend, insert '/' instead of '((BASE URL)))'.
34 proxy_set_header X-Forwarded-Proto $scheme;
35 proxy_set_header Host $host;
36 proxy_http_version 1.1;
37 proxy_pass http://((IP)):((PORT));
38 }
39}
40```
41Pretty 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.
42## How do I add my instance to the list?
43To 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:
441. the Instance must not use Cloudflare. 151. the Instance must not use Cloudflare.
452. If your instance has modified source code, you need to publish it to any free platform. For example, Github and Gitlab are not. 162. If your instance has modified source code, you need to publish it to any free platform. For example, Github and Gitlab are not.
46## Acknowledgements 17## Acknowledgements
47* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) -- helped me understand Go and gave me a lot of useful advice on this language. 18* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — helped me understand Go and gave me a lot of useful advice on this language.
19* [meoww](https://codeberg.org/meoww) — translated some sentences into English and wrote a service for openrc
48 20
49# RU 🇷🇺 21# RU 🇷🇺
50## Описание 22## Описание
51SkunkyArt 🦨 -- альтернативный фронтенд к DeviantArt, который будет работать без проблем даже на довольно старом оборудовании, за счёт отсутствия JavaScript. 23SkunkyArt 🦨 — альтернативный фронтенд к DeviantArt, который полностью работает без JS (JavaScript).
52## Конфиг 24## Настройка
53Пример конфига находится в файле `config.example.json`. Чтобы указать свой путь до конфига, используйте CLI-аргумент `-c` или `--config`. 25Пример конфига находится в файле `config.example.json`. Чтобы указать свой конфиг, используйте cli-аргумент `--config`.
54* `listen` -- адрес и порт, на котором будет слушать SkunkyArt 26См. [`SETUP-RU.md`](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP-RU.md) для информации о настройки фронтенда.
55* `base-path` -- путь к инстансу. Пример: `"base-path": "/art/"` -> https://skunky.ebloid.ru/art/ 27## Добавление инстанса в список
56* `cache` -- система кеширования; по умолчанию - выкл. 28Чтобы это сделать, вы должны либо сделать PR, добавив в файлы `instances.json` и `INSTANCES.md` свой инстанс (можете воспользоваться cli-аргументом `--add-instance`, который автоматически это сделает), либо создать Issue, или сообщить о нём в комнате в Matrix. Учтите, что ваш инстанс должен соблюсти следущие правила:
57 * `path` -- путь до кеша 291. Инстанс не должен использовать Cloudflare итп.
58 * `lifetime` -- время жизни файла в кеше. Единицы измерения: i, h, d, w, m, y. I -- минута, всё остальные единицы измерения, я считаю понятными и без объяснения.
59 * `max-size` -- максимальный размер файла в мегабайтах
60* `dirs-to-memory` -- данная настройка определяет какие каталоги будут скопированы в ОЗУ при запуске SkunkyArt. Обязательна
61* `download-proxy` -- адрес прокси для загрузки файлов
62## Примеры reverse-прокси
63Nginx:
64```apache
65server {
66 listen 443 ssl;
67 server_name skunky.example.com;
68
69 location ((BASE URL)) { # если у вас отдельный поддомен для фронтенда, вместо '((BASE URL))' вставляйте '/'
70 proxy_set_header X-Forwarded-Proto $scheme;
71 proxy_set_header Host $host;
72 proxy_http_version 1.1;
73 proxy_pass http://((IP)):((PORT));
74 }
75}
76```
77В целом, всё как обычно, за исключением настройки заголовка [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto).
78## Как добавить свой инстанс в список?
79Чтобы это сделать, вы должны либо сделать PR, добавив в файлы `instances.json` и `README.md` свой инстанс, либо создать Issue, или сообщить о нём в комнате в Matrix. Думаю, он не нуждается в описании. Однако учтите, у этого списка есть пара правил:
801. Инстанс не должен использовать Cloudflare.
812. Если ваш инстанс имеет модифицированный исходный код, то вам нужно опубликовать его на любую свободную площадку. Например, Github и Gitlab таковыми не являются. 302. Если ваш инстанс имеет модифицированный исходный код, то вам нужно опубликовать его на любую свободную площадку. Например, Github и Gitlab таковыми не являются.
82## Благодарности 31## Благодарности
83* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) -- помог разобраться в Go и много чего полезного посоветовал по этому языку.\ No newline at end of file 32* [Лис⚛](https://go.kde.org/matrix/#/@fox:matrix.org) — помог разобраться в Go и много чего полезного посоветовал по этому языку.
33* [meoww](https://codeberg.org/meoww) — перевела некоторые предложения на английский язык и написала сервис для openrc\ No newline at end of file
SETUP-RU.md added +42
@@ -0,0 +1,42 @@
1[English version 🇬🇧](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP.md)
2
3# Единицы измерения
4Размер файла в кеше измеряется в мегабайтах.<br>
5Единицы времени:
6* `i` — минуты
7* `h` — часы
8* `w` — недели
9* `m` — месяца
10* `y` — года
11
12# Конфигурация
13* `listen` — IP и порт для слушанья; заполняется по такой форме: ip:port
14* `uri` — URI инстанса. Пример: `"uri":"/art/"` -> https://skunky.ebloid.ru/art/
15* `cache` — Система кеширования; по умолчанию выключена.
16 * `enabled` — Состояние системы кеширования; требуется булёвое значение
17 * `path` — Полный путь до каталога, куда будет сохраняться кеш
18 * `lifetime` — Время жизни файла в кеше, требует целочисленное значение, дополненное суффиксом времени (см. 'Единицы времени')
19 * `max-size` — Максимальный размер файла
20 * `update-interval` — Интервал для автоматической ротации кеша
21* `dirs-to-memory` — Массив, заполнив который скопируются все файлы из указанных каталогов
22* `download-proxy` — Адрес прокси для загрузки файлов
23* `user-agent` — Строка, которая используется в качестве User-Agent'а
24
25# Настройка обратного прокси
26Если вы собираетесь хостить инстанс в Интернете, то вам следует настроить заголовок прокси [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto). В противном случае, все ссылки на вашем инстансе будут вида "http". Ниже есть информация о том, как настроить обратное проксирование:
27
28Nginx:
29```apache
30server {
31 listen 443 ssl;
32 server_name skunky.example.com;
33
34 # Если используется поддомен, то вместо ((BASE_URL)), укажите '/'.
35 location ((BASE_URL)) {
36 proxy_set_header X-Forwarded-Proto $scheme;
37 proxy_set_header Host $host;
38 proxy_http_version 1.1;
39 proxy_pass http://((IP)):((PORT));
40 }
41}
42```\ No newline at end of file
SETUP.md added +42
@@ -0,0 +1,42 @@
1[Версия на русском языке 🇷🇺](https://git.macaw.me/skunky/SkunkyArt/src/branch/master/SETUP-RU.md)
2
3# Units
4Maximum file size in megabytes, requires numeric value.<br>
5Time units:
6* `i` — minutes
7* `h` — hours
8* `w` — weeks
9* `m` — months
10* `y` — years
11
12# Config
13* `listen` — IP and port to listen on in the following form: ip:port
14* `uri` — Instance URI. Example: `"uri":"/art/"` -> https://skunky.ebloid.ru/art/
15* `cache` — Caching system; default is off.
16 * `enabled` — Caching system state, requires boolean value
17 * `path` — Path to cache directory, requires absolute filesystem path
18 * `lifetime` — Cached file life time, requires numeric value, followed by multiplicative suffix (see Time Units for details)
19 * `max-size` — Maximum file size in megabytes
20 * `update-interval` — Automatic rotation interval
21* `dirs-to-memory` — This setting determines which directories will be copied to RAM when SkunkyArt is started. Mandatory
22* `download-proxy` — Proxy address for downloading files.
23* `user-agent` — String, which SkunkyArt uses as UA
24
25# Setting up reverse proxy
26Pretty 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.
27
28Nginx example configuration:
29```apache
30server {
31 listen 443 ssl;
32 server_name skunky.example.com;
33
34 # In case of subdomain, use / instend of ((BASE_URL))
35 location ((BASE_URL)) {
36 proxy_set_header X-Forwarded-Proto $scheme;
37 proxy_set_header Host $host;
38 proxy_http_version 1.1;
39 proxy_pass http://((IP)):((PORT));
40 }
41}
42```\ No newline at end of file
TODO.md +4 −1
@@ -1,14 +1,17 @@
1# v1.3.x 1# v1.3.x
2* Написать Makefile
3* Почистить говнокод 2* Почистить говнокод
4* **Доделать парсинг описания** 3* **Доделать парсинг описания**
5* Избавиться от хардкода под Linux 4* Избавиться от хардкода под Linux
6* ~~Реализовать стрипы в ежедневных артах~~ 5* ~~Реализовать стрипы в ежедневных артах~~
6* Сделать нормальное отображение ошибок
7* ~~Исправить баг с навигацией по страницам~~ 7* ~~Исправить баг с навигацией по страницам~~
8* ~~Сделать единицы в конфиге более понятными~~ 8* ~~Сделать единицы в конфиге более понятными~~
9* Добавить возможность включить темплейты в бинарник 9* Добавить возможность включить темплейты в бинарник
10* ~~Реализовать миниатюры и оптимизировать CSS под маленькие экраны~~ 10* ~~Реализовать миниатюры и оптимизировать CSS под маленькие экраны~~
11* Написать Makefile и скрипт для автоматического развёртывания инстанса
11* **Реализовать отображение контента, отличного от картинок (видео, аудио, etc)** 12* **Реализовать отображение контента, отличного от картинок (видео, аудио, etc)**
13* Исправить баг с эмоджи, когда некоторые кастомные эмоции могут не отображаться
14* Добавить флаг сборки, который позволит собрать бинарник со встроенными темплейтами
12* Улучшить систему кеширования: добавить рейтинг для удаления и копирование изображений в ОЗУ 15* Улучшить систему кеширования: добавить рейтинг для удаления и копирование изображений в ОЗУ
13# v1.4 16# v1.4
14* Реализовать API 17* Реализовать API
app/cli.go added +166
@@ -0,0 +1,166 @@
1package app
2
3import (
4 "bufio"
5 "bytes"
6 "encoding/json"
7 "os"
8 "time"
9)
10
11func ExecuteCommandLineArguments() {
12 const helpmsg = `SkunkyArt v1.3.1 [CSS improvements for mobile and the strips on Daily Deviations]
13Usage:
14 - [-c|--config] | path to config
15 - [-a|--add-instance] | generates 'instances.json' and 'INSTANCES.md' files with ur instance
16 - [-h|--help] | returns this message
17Example:
18 ./skunkyart -c config.json
19Copyright lost+skunk, X11. https://git.macaw.me/skunky/skunkyart/src/tag/v1.3.1`
20
21 a := os.Args[1:]
22 for n, x := range a {
23 switch x {
24 case "-c", "--config":
25 if len(a) >= 2 {
26 CFG.cfg = a[n+1]
27 } else {
28 exit("Not enought arguments", 1)
29 }
30 case "-h", "--help":
31 exit(helpmsg, 0)
32 case "-a", "--add-instance":
33 addInstance()
34 }
35 }
36}
37
38type settingsUrls struct {
39 I2P string `json:"i2p,omitempty"`
40 Ygg string `json:"ygg,omitempty"`
41 Tor string `json:"tor,omitempty"`
42 Clearnet string `json:"clearnet,omitempty"`
43}
44
45type settingsParams struct {
46 Nsfw bool `json:"nsfw"`
47 Proxy bool `json:"proxy"`
48}
49
50type settings struct {
51 Title string `json:"title"`
52 Country string `json:"country"`
53 ModifiedSrc string `json:"modified-src,omitempty"`
54 Urls settingsUrls `json:"urls"`
55 Settings settingsParams `json:"settings"`
56}
57
58func addInstance() {
59 prompt := func(txt string, necessary bool) string {
60 input := bufio.NewScanner(os.Stdin)
61 for {
62 print(txt)
63 print(": ")
64 input.Scan()
65
66 if i := input.Text(); necessary && i == "" {
67 println("Please specify the", txt)
68 } else {
69 return i
70 }
71 }
72 }
73
74 var settingsVar struct {
75 Instances []settings `json:"instances"`
76 }
77 instancesJson, err := os.OpenFile("instances.test.json", os.O_CREATE|os.O_WRONLY, 0644)
78 try(err)
79 defer instancesJson.Close()
80
81 instances, err := os.OpenFile("INSTANCES.md", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
82 try(err)
83 defer instances.Close()
84
85 for {
86 if Templates["instances.json"] == "" {
87 print("\rDownloading instance list...")
88 } else {
89 println("\r\033[2KDownloaded!")
90 try(json.Unmarshal([]byte(Templates["instances.json"]), &settingsVar))
91
92 settingsVar.Instances = append(settingsVar.Instances, settings{
93 Title: prompt("Title", true),
94 Country: prompt("Country", true),
95 ModifiedSrc: prompt("Link to modified sources", false),
96 Settings: settingsParams{
97 Nsfw: CFG.Nsfw,
98 Proxy: CFG.Proxy,
99 },
100 Urls: settingsUrls{
101 Clearnet: prompt("Clearnet link", false),
102 Ygg: prompt("Yggdrasil link", false),
103 Tor: prompt("Onion link", false),
104 I2P: prompt("I2P link", false),
105 },
106 })
107
108 j, err := json.MarshalIndent(&settingsVar, "", " ")
109 try(err)
110
111 instancesJson.Write(j)
112
113 settingsVar := &settingsVar.Instances[len(settingsVar.Instances)-1]
114 var mdstr bytes.Buffer
115
116 mdstr.WriteString("\n|")
117 if settingsVar.Urls.Clearnet != "" {
118 mdstr.WriteString("[")
119 mdstr.WriteString(settingsVar.Title)
120 mdstr.WriteString("](")
121 mdstr.WriteString(settingsVar.Urls.Clearnet)
122 mdstr.WriteString(")")
123 } else {
124 mdstr.WriteString(settingsVar.Title)
125 }
126 mdstr.WriteString("|")
127
128 urls := []string{settingsVar.Urls.Ygg, settingsVar.Urls.I2P, settingsVar.Urls.Tor}
129 for i, l := 0, len(urls); i < l; i++ {
130 url := urls[i]
131 if url != "" {
132 mdstr.WriteString("[Yes](")
133 mdstr.WriteString(url)
134 mdstr.WriteString(")|")
135 } else {
136 mdstr.WriteString("No|")
137 }
138 }
139
140 settings := []bool{settingsVar.Settings.Nsfw, settingsVar.Settings.Proxy}
141 for i, l := 0, len(settings); i < l; i++ {
142 if settings[i] {
143 mdstr.WriteString("Yes|")
144 } else {
145 mdstr.WriteString("No|")
146 }
147 }
148
149 if settingsVar.ModifiedSrc != "" {
150 mdstr.WriteString("[Yes](")
151 mdstr.WriteString(settingsVar.ModifiedSrc)
152 mdstr.WriteString(")|")
153 } else {
154 mdstr.WriteString("No|")
155 }
156
157 mdstr.WriteString(settingsVar.Country)
158 mdstr.WriteString("|")
159
160 instances.Write(mdstr.Bytes())
161 break
162 }
163 time.Sleep(500 * time.Millisecond)
164 }
165 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)
166}
app/config.go +6 −42
@@ -21,7 +21,7 @@ type cache_config struct {
21type config struct { 21type config struct {
22 cfg string 22 cfg string
23 Listen string 23 Listen string
24 BasePath string `json:"base-path"` 24 URI string `json:"uri"`
25 Cache cache_config 25 Cache cache_config
26 Proxy, Nsfw bool 26 Proxy, Nsfw bool
27 UserAgent string `json:"user-agent"` 27 UserAgent string `json:"user-agent"`
@@ -30,15 +30,15 @@ type config struct {
30} 30}
31 31
32var CFG = config{ 32var CFG = config{
33 cfg: "config.json", 33 cfg: "config.json",
34 Listen: "127.0.0.1:3003", 34 Listen: "127.0.0.1:3003",
35 BasePath: "/", 35 URI: "/",
36 Cache: cache_config{ 36 Cache: cache_config{
37 Enabled: true, 37 Enabled: false,
38 Path: "cache", 38 Path: "cache",
39 UpdateInterval: 1, 39 UpdateInterval: 1,
40 }, 40 },
41 Dirs: []string{"html", "css"}, 41 Dirs: []string{"html", "css", "misc"},
42 UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", 42 UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
43 Proxy: true, 43 Proxy: true,
44 Nsfw: true, 44 Nsfw: true,
@@ -47,42 +47,6 @@ var CFG = config{
47var lifetimeParsed int64 47var lifetimeParsed int64
48 48
49func ExecuteConfig() { 49func ExecuteConfig() {
50 go func() {
51 for {
52 func() {
53 defer func() {
54 if r := recover(); r != nil {
55 recover()
56 }
57 }()
58 Templates["instances.json"] = string(Download("https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/instances.json").Body)
59 }()
60 time.Sleep(1 * time.Hour)
61 }
62 }()
63
64 const helpmsg = `SkunkyArt v1.3.1 [CSS improvements for mobile and strips on Daily Deviations]
65Usage:
66 - [-c|--config] - path to config
67 - [-h|--help] - returns this message
68Example:
69 ./skunkyart -c config.json
70Copyright lost+skunk, X11. https://git.macaw.me/skunky/skunkyart/src/tag/v1.3.1`
71
72 a := os.Args
73 for n, x := range a {
74 switch x {
75 case "-c", "--config":
76 if len(a) >= 3 {
77 CFG.cfg = a[n+1]
78 } else {
79 exit("Not enought arguments", 1)
80 }
81 case "-h", "--help":
82 exit(helpmsg, 0)
83 }
84 }
85
86 if CFG.cfg != "" { 50 if CFG.cfg != "" {
87 f, err := os.ReadFile(CFG.cfg) 51 f, err := os.ReadFile(CFG.cfg)
88 tryWithExitStatus(err, 1) 52 tryWithExitStatus(err, 1)
app/router.go +3 −3
@@ -11,7 +11,7 @@ var Host string
11 11
12func Router() { 12func Router() {
13 parsepath := func(path string) map[int]string { 13 parsepath := func(path string) map[int]string {
14 if l := len(CFG.BasePath); len(path) > l { 14 if l := len(CFG.URI); len(path) > l {
15 path = path[l-1:] 15 path = path[l-1:]
16 } else { 16 } else {
17 path = "/" 17 path = "/"
@@ -54,7 +54,7 @@ func Router() {
54 var skunky skunkyart 54 var skunky skunkyart
55 skunky.Writer = w 55 skunky.Writer = w
56 skunky.Args = r.URL.Query() 56 skunky.Args = r.URL.Query()
57 skunky.BasePath = CFG.BasePath 57 skunky.BasePath = CFG.URI
58 58
59 arg := skunky.Args.Get 59 arg := skunky.Args.Get
60 skunky.QueryRaw = arg("q") 60 skunky.QueryRaw = arg("q")
@@ -75,7 +75,7 @@ func Router() {
75 default: 75 default:
76 skunky.ReturnHTTPError(404) 76 skunky.ReturnHTTPError(404)
77 case "": 77 case "":
78 skunky.ExecuteTemplate("index.htm", &CFG.BasePath) 78 skunky.ExecuteTemplate("index.htm", &CFG.URI)
79 case "post": 79 case "post":
80 skunky.Deviation(path[2], path[3]) 80 skunky.Deviation(path[2], path[3])
81 case "search": 81 case "search":
app/util.go +18 −5
@@ -32,6 +32,20 @@ func tryWithExitStatus(err error, code int) {
32 } 32 }
33} 33}
34 34
35func RefreshInstances() {
36 for {
37 func() {
38 defer func() {
39 if r := recover(); r != nil {
40 recover()
41 }
42 }()
43 Templates["instances.json"] = string(Download("https://git.macaw.me/skunky/SkunkyArt/raw/branch/master/instances.json").Body)
44 }()
45 time.Sleep(1 * time.Hour)
46 }
47}
48
35// some crap for frontend 49// some crap for frontend
36func (s skunkyart) ExecuteTemplate(file string, data any) { 50func (s skunkyart) ExecuteTemplate(file string, data any) {
37 var buf strings.Builder 51 var buf strings.Builder
@@ -46,7 +60,7 @@ func UrlBuilder(strs ...string) string {
46 var str strings.Builder 60 var str strings.Builder
47 l := len(strs) 61 l := len(strs)
48 str.WriteString(Host) 62 str.WriteString(Host)
49 str.WriteString(CFG.BasePath) 63 str.WriteString(CFG.URI)
50 for n, x := range strs { 64 for n, x := range strs {
51 str.WriteString(x) 65 str.WriteString(x)
52 if n+1 < l && !(strs[n+1][0] == '?' || strs[n+1][0] == '&') && !(x[0] == '?' || x[0] == '&') { 66 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 {
220 234
221func GetValueOfTag(t *html.Tokenizer) string { 235func GetValueOfTag(t *html.Tokenizer) string {
222 for tt := t.Next(); ; { 236 for tt := t.Next(); ; {
223 switch tt { 237 if tt == html.TextToken {
224 default:
225 return ""
226 case html.TextToken:
227 return string(t.Text()) 238 return string(t.Text())
239 } else {
240 return ""
228 } 241 }
229 } 242 }
230} 243}
app/wrapper.go +44 −56
@@ -31,20 +31,7 @@ type skunkyart struct {
31 About struct { 31 About struct {
32 Proxy bool 32 Proxy bool
33 Nsfw bool 33 Nsfw bool
34 Instances []struct { 34 Instances []settings
35 Title string
36 Country string
37 Urls []struct {
38 I2P string `json:"i2p"`
39 Ygg string
40 Tor string
41 Clearnet string
42 }
43 Settings struct {
44 Nsfw bool
45 Proxy bool
46 }
47 }
48 } 35 }
49 36
50 SomeList string 37 SomeList string
@@ -229,48 +216,49 @@ func (s skunkyart) GRUser() {
229// посты 216// посты
230func (s skunkyart) Deviation(author, postname string) { 217func (s skunkyart) Deviation(author, postname string) {
231 id_search := regexp.MustCompile("[0-9]+").FindAllString(postname, -1) 218 id_search := regexp.MustCompile("[0-9]+").FindAllString(postname, -1)
232 if len(id_search) >= 1 { 219 if len(id_search) < 1 {
233 post := &s.Templates.Deviation 220 s.ReturnHTTPError(400)
221 return
222 }
234 223
235 id := id_search[len(id_search)-1] 224 post := &s.Templates.Deviation
236 post.Post = devianter.GetDeviation(id, author)
237 225
238 if post.Post.Deviation.TextContent.Excerpt != "" { 226 id := id_search[len(id_search)-1]
239 post.Post.Description = ParseDescription(post.Post.Deviation.TextContent) 227 post.Post = devianter.GetDeviation(id, author)
240 } else {
241 post.Post.Description = ParseDescription(post.Post.Deviation.Extended.DescriptionText)
242 }
243 // время публикации
244 post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
245 post.Post.IMG = ParseMedia(post.Post.Deviation.Media)
246 for _, x := range post.Post.Deviation.Extended.RelatedContent {
247 if len(x.Deviations) != 0 {
248 post.Related += s.DeviationList(x.Deviations, false)
249 }
250 }
251 228
252 // хештэги 229 if post.Post.Deviation.TextContent.Excerpt != "" {
253 for _, x := range post.Post.Deviation.Extended.Tags { 230 post.Post.Description = ParseDescription(post.Post.Deviation.TextContent)
254 var tag strings.Builder 231 } else {
255 tag.WriteString(` <a href="`) 232 post.Post.Description = ParseDescription(post.Post.Deviation.Extended.DescriptionText)
256 tag.WriteString(UrlBuilder("search", "?q=", x.Name, "&type=tag")) 233 }
257 tag.WriteString(`">#`) 234 // время публикации
258 tag.WriteString(x.Name) 235 post.StringTime = post.Post.Deviation.PublishedTime.UTC().String()
259 tag.WriteString("</a>") 236 post.Post.IMG = ParseMedia(post.Post.Deviation.Media)
260 237 for _, x := range post.Post.Deviation.Extended.RelatedContent {
261 post.Tags += tag.String() 238 if len(x.Deviations) != 0 {
239 post.Related += s.DeviationList(x.Deviations, false)
262 } 240 }
241 }
263 242
264 if post.Post.Comments.Total <= 50 { 243 // хештэги
265 post.Post.Comments.Cursor = "" 244 for _, x := range post.Post.Deviation.Extended.Tags {
266 } 245 var tag strings.Builder
246 tag.WriteString(` <a href="`)
247 tag.WriteString(UrlBuilder("search", "?q=", x.Name, "&type=tag"))
248 tag.WriteString(`">#`)
249 tag.WriteString(x.Name)
250 tag.WriteString("</a>")
267 251
268 post.Comments = s.ParseComments(devianter.GetComments(id, post.Post.Comments.Cursor, s.Page, 1)) 252 post.Tags += tag.String()
253 }
269 254
270 s.ExecuteTemplate("deviantion.htm", &s) 255 if post.Post.Comments.Total <= 50 {
271 } else { 256 post.Post.Comments.Cursor = ""
272 s.ReturnHTTPError(400)
273 } 257 }
258
259 post.Comments = s.ParseComments(devianter.GetComments(id, post.Post.Comments.Cursor, s.Page, 1))
260
261 s.ExecuteTemplate("deviantion.htm", &s)
274} 262}
275 263
276func (s skunkyart) DD() { 264func (s skunkyart) DD() {
@@ -298,7 +286,6 @@ func (s skunkyart) DD() {
298} 286}
299 287
300func (s skunkyart) Search() { 288func (s skunkyart) Search() {
301 s.Atom = false
302 var err error 289 var err error
303 ss := &s.Templates.Search 290 ss := &s.Templates.Search
304 switch s.Type { 291 switch s.Type {
@@ -365,15 +352,16 @@ func (s skunkyart) Search() {
365} 352}
366 353
367func (s skunkyart) Emojitar(name string) { 354func (s skunkyart) Emojitar(name string) {
368 if name != "" && (s.Type == 'a' || s.Type == 'e') { 355 if name == "" || !(s.Type == 'a' || s.Type == 'e') {
369 ae, e := devianter.AEmedia(name, s.Type)
370 if e != nil {
371 s.ReturnHTTPError(404)
372 }
373 wr(s.Writer, ae)
374 } else {
375 s.ReturnHTTPError(400) 356 s.ReturnHTTPError(400)
357 return
358 }
359
360 ae, e := devianter.AEmedia(name, s.Type)
361 if e != nil {
362 s.ReturnHTTPError(404)
376 } 363 }
364 wr(s.Writer, ae)
377} 365}
378 366
379func (s skunkyart) About() { 367func (s skunkyart) About() {
config.example.json +7 −7
@@ -1,17 +1,17 @@
1{ 1{
2 "listen": "0.0.0.0:3003", 2 "listen": "0:3003",
3 "base-path": "/", 3 "uri": "/",
4 "cache": { 4 "cache": {
5 "enabled": true, 5 "enabled": true,
6 "path": "/home/skunk/projects/skunkyart/cache", 6 "path": "cache",
7 "lifetime": "1w", 7 "lifetime": null,
8 "max-size": 1024, 8 "max-size": 1024,
9 "update-interval": 5 9 "update-interval": 5
10 }, 10 },
11 "dirs-to-memory": [ 11 "dirs-to-memory": [
12 "/home/skunk/projects/skunkyart/html", 12 "html",
13 "/home/skunk/projects/skunkyart/css", 13 "css",
14 "/home/skunk/projects/skunkyart/misc" 14 "misc"
15 ], 15 ],
16 "download-proxy": "http://127.0.0.1:8080", 16 "download-proxy": "http://127.0.0.1:8080",
17 "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", 17 "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",
go.mod +1 −3
@@ -2,9 +2,7 @@ module skunkyart
2 2
3go 1.22.3 3go 1.22.3
4 4
5replace git.macaw.me/skunky/devianter v0.2.0 => /home/skunk/projects/devianter
6
7require ( 5require (
8 git.macaw.me/skunky/devianter v0.2.0 6 git.macaw.me/skunky/devianter v0.2.5
9 golang.org/x/net v0.27.0 7 golang.org/x/net v0.27.0
10) 8)
go.sum +2 −2
@@ -1,4 +1,4 @@
1git.macaw.me/skunky/devianter v0.2.0 h1:2vnMPb1Dax37CbAOfmHcSoK8+1goFkWHbtbh31Ytsww= 1git.macaw.me/skunky/devianter v0.2.5 h1:aAc6CG/ghvG130Ob7gGUdK4IV3MSeCD5t3QIJjto1M0=
2git.macaw.me/skunky/devianter v0.2.0/go.mod h1:ZLn527xBlnpXrUB1B8z/MhyeiWVK4nPWjyfnhWOE8Is= 2git.macaw.me/skunky/devianter v0.2.5/go.mod h1:ZLn527xBlnpXrUB1B8z/MhyeiWVK4nPWjyfnhWOE8Is=
3golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= 3golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
4golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 4golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
html/about.htm +32 −31
@@ -3,6 +3,7 @@
3 <head> 3 <head>
4 <title>SkunkyArt</title> 4 <title>SkunkyArt</title>
5 <link rel="stylesheet" href="{{.BasePath}}stylesheet"> 5 <link rel="stylesheet" href="{{.BasePath}}stylesheet">
6 <link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
6 </head> 7 </head>
7 <main> 8 <main>
8 <header> 9 <header>
@@ -21,43 +22,43 @@
21 SkunkyArt is an alternative frontend for deviantart.com, written in Go. 22 SkunkyArt is an alternative frontend for deviantart.com, written in Go.
22 </p> 23 </p>
23 <h3><a href="https://go.kde.org/matrix/#/#skunkyart:ebloid.ru" target="_blank">Room in Matrix</a></h3> 24 <h3><a href="https://go.kde.org/matrix/#/#skunkyart:ebloid.ru" target="_blank">Room in Matrix</a></h3>
24 <h2>Instance settings:</h2> 25 <b>Instance settings:</b>
25 <ul> 26 <ul>
26 <li><b>NSFW</b>: <span class="about-{{.Templates.About.Nsfw}}">{{if .Templates.About.Nsfw}}YES{{else}}NO{{end}}</span></li> 27 <li><b>NSFW</b>: <span class="about-{{.Templates.About.Nsfw}}">{{if .Templates.About.Nsfw}}YES{{else}}NO{{end}}</span></li>
27 <li><b>Proxyfing</b>: <span class="about-{{.Templates.About.Proxy}}">{{if .Templates.About.Proxy}}YES{{else}}NO{{end}}</span></li> 28 <li><b>Proxyfing</b>: <span class="about-{{.Templates.About.Proxy}}">{{if .Templates.About.Proxy}}YES{{else}}NO{{end}}</span></li>
28 </ul> 29 </ul>
29 <h2>Instances:</h2> 30 <details>
30 <ul> 31 <summary><b>Instances:</b></summary>
31 {{range .Templates.About.Instances}} 32 <ul>
32 <li><u><b>{{.Title}}</b></u>: 33 {{range .Templates.About.Instances}}
33 <ul> 34 <li><u><b>{{.Title}}</b></u>:
34 <li><b>Country</b>: {{.Country}}</li>
35 <li><b>URLs</b>: </li>
36 <ul>
37 {{range .Urls}}
38 {{if ne .I2P ""}}
39 <li><b>I2P</b>: <a href="{{.I2P}}">Yes</a></li>
40 {{end}}
41 {{if ne .Ygg ""}}
42 <li><b>Ygg</b>: <a href="{{.Ygg}}">Yes</a></li>
43 {{end}}
44 {{if ne .Tor ""}}
45 <li><b>Tor</b>: <a href="{{.Tor}}">Yes</a></li>
46 {{end}}
47 {{if ne .Clearnet ""}}
48 <li><b>Clearnet</b>: <a href="{{.Clearnet}}">{{.Clearnet}}</a></li>
49 {{end}}
50 {{end}}
51 </ul>
52 <li><b>Settings</b>: </li>
53 <ul> 35 <ul>
54 <li><b>NSFW</b>: <span class="about-{{.Settings.Nsfw}}">{{if .Settings.Nsfw}}YES{{else}}NO{{end}}</span></li> 36 <li><b>Country</b>: {{.Country}}</li>
55 <li><b>Proxyfing</b>: <span class="about-{{.Settings.Proxy}}">{{if .Settings.Proxy}}YES{{else}}NO{{end}}</span></li> 37 <li><b>URLs</b>: </li>
38 <ul>
39 {{if ne .Urls.I2P ""}}
40 <li><b>I2P</b>: <a href="{{.Urls.I2P}}">Yes</a></li>
41 {{end}}
42 {{if ne .Urls.Ygg ""}}
43 <li><b>Ygg</b>: <a href="{{.Urls.Ygg}}">Yes</a></li>
44 {{end}}
45 {{if ne .Urls.Tor ""}}
46 <li><b>Tor</b>: <a href="{{.Urls.Tor}}">Yes</a></li>
47 {{end}}
48 {{if ne .Urls.Clearnet ""}}
49 <li><b>Clearnet</b>: <a href="{{.Urls.Clearnet}}">{{.Urls.Clearnet}}</a></li>
50 {{end}}
51 </ul>
52 <li><b>Settings</b>: </li>
53 <ul>
54 <li><b>NSFW</b>: <span class="about-{{.Settings.Nsfw}}">{{if .Settings.Nsfw}}YES{{else}}NO{{end}}</span></li>
55 <li><b>Proxyfing</b>: <span class="about-{{.Settings.Proxy}}">{{if .Settings.Proxy}}YES{{else}}NO{{end}}</span></li>
56 </ul>
56 </ul> 57 </ul>
57 </ul> 58 </li>
58 </li> 59 {{end}}
59 {{end}} 60 </ul>
60 </ul> 61 </details>
61 <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> 62 <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>
62 </main> 63 </main>
63</html>\ No newline at end of file 64</html>\ No newline at end of file
html/daily.htm +1
@@ -3,6 +3,7 @@
3 <head> 3 <head>
4 <title>SkunkyArt | Daily Deviations</title> 4 <title>SkunkyArt | Daily Deviations</title>
5 <link rel="stylesheet" href="{{.BasePath}}stylesheet"> 5 <link rel="stylesheet" href="{{.BasePath}}stylesheet">
6 <link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
6 </head> 7 </head>
7 <main> 8 <main>
8 <header> 9 <header>
html/deviantion.htm +1
@@ -4,6 +4,7 @@
4 <title>SkunkyArt | {{.Templates.Deviation.Post.Deviation.Author.Username}} - {{.Templates.Deviation.Post.Deviation.Title}}</title> 4 <title>SkunkyArt | {{.Templates.Deviation.Post.Deviation.Author.Username}} - {{.Templates.Deviation.Post.Deviation.Title}}</title>
5 <link rel="stylesheet" href="{{.BasePath}}stylesheet"> 5 <link rel="stylesheet" href="{{.BasePath}}stylesheet">
6 <meta name="referrer" content="no-referrer" /> 6 <meta name="referrer" content="no-referrer" />
7 <link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
7 </head> 8 </head>
8 <main> 9 <main>
9 <header> 10 <header>
html/gruser.htm +1
@@ -9,6 +9,7 @@
9 {{end}} 9 {{end}}
10 </title> 10 </title>
11 <link rel="stylesheet" href="{{.BasePath}}stylesheet"> 11 <link rel="stylesheet" href="{{.BasePath}}stylesheet">
12 <link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
12 </head> 13 </head>
13 <main> 14 <main>
14 <header> 15 <header>
html/index.htm +1
@@ -3,6 +3,7 @@
3 <head> 3 <head>
4 <title>SkunkyArt</title> 4 <title>SkunkyArt</title>
5 <link rel="stylesheet" href="{{.}}stylesheet"/> 5 <link rel="stylesheet" href="{{.}}stylesheet"/>
6 <link rel="icon" type="image/x-icon" href="{{.}}favicon.ico">
6 </head> 7 </head>
7 <main> 8 <main>
8 <center> 9 <center>
html/search.htm +1
@@ -3,6 +3,7 @@
3 <head> 3 <head>
4 <title>SkunkyArt | Search "{{.QueryRaw}}"</title> 4 <title>SkunkyArt | Search "{{.QueryRaw}}"</title>
5 <link rel="stylesheet" href="{{.BasePath}}stylesheet"> 5 <link rel="stylesheet" href="{{.BasePath}}stylesheet">
6 <link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
6 </head> 7 </head>
7 <main> 8 <main>
8 <header> 9 <header>
instances.json +10 −10
@@ -3,10 +3,10 @@
3 { 3 {
4 "title": "skunky.ebloid.ru", 4 "title": "skunky.ebloid.ru",
5 "country": "Russia", 5 "country": "Russia",
6 "urls": [{ 6 "urls": {
7 "ygg": "http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art", 7 "ygg": "http://[201:eba5:d1fc:bf7b:cfcb:a811:4b8b:7ea3]/art",
8 "clearnet": "https://skunky.ebloid.ru/art" 8 "clearnet": "https://skunky.ebloid.ru/art"
9 }], 9 },
10 "settings": { 10 "settings": {
11 "nsfw": false, 11 "nsfw": false,
12 "proxy": false 12 "proxy": false
@@ -15,9 +15,9 @@
15 { 15 {
16 "title": "clovius.club", 16 "title": "clovius.club",
17 "country": "Sweden", 17 "country": "Sweden",
18 "urls": [{ 18 "urls": {
19 "clearnet": "https://skunky.clovius.club" 19 "clearnet": "https://skunky.clovius.club"
20 }], 20 },
21 "settings": { 21 "settings": {
22 "nsfw": true, 22 "nsfw": true,
23 "proxy": true 23 "proxy": true
@@ -26,9 +26,9 @@
26 { 26 {
27 "title": "bloat.cat", 27 "title": "bloat.cat",
28 "country": "Romania", 28 "country": "Romania",
29 "urls": [{ 29 "urls": {
30 "clearnet": "https://skunky.bloat.cat" 30 "clearnet": "https://skunky.bloat.cat"
31 }], 31 },
32 "settings": { 32 "settings": {
33 "nsfw": true, 33 "nsfw": true,
34 "proxy": true 34 "proxy": true
@@ -37,9 +37,9 @@
37 { 37 {
38 "title": "frontendfriendly.xyz", 38 "title": "frontendfriendly.xyz",
39 "country": "Finland", 39 "country": "Finland",
40 "urls": [{ 40 "urls": {
41 "clearnet": "https://skunkyart.frontendfriendly.xyz" 41 "clearnet": "https://skunkyart.frontendfriendly.xyz"
42 }], 42 },
43 "settings": { 43 "settings": {
44 "nsfw": true, 44 "nsfw": true,
45 "proxy": true 45 "proxy": true
@@ -48,9 +48,9 @@
48 { 48 {
49 "title": "lumaeris.com", 49 "title": "lumaeris.com",
50 "country": "US", 50 "country": "US",
51 "urls": [{ 51 "urls": {
52 "clearnet": "https://skunkyart.lumaeris.com" 52 "clearnet": "https://skunkyart.lumaeris.com"
53 }], 53 },
54 "settings": { 54 "settings": {
55 "nsfw": true, 55 "nsfw": true,
56 "proxy": true 56 "proxy": true
main.go +14 −4
@@ -2,17 +2,27 @@ package main
2 2
3import ( 3import (
4 "skunkyart/app" 4 "skunkyart/app"
5 "time"
5 6
6 "git.macaw.me/skunky/devianter" 7 "git.macaw.me/skunky/devianter"
7) 8)
8 9
9func main() { 10func main() {
11 go app.RefreshInstances()
12
13 app.ExecuteCommandLineArguments()
10 app.ExecuteConfig() 14 app.ExecuteConfig()
11 app.CopyTemplatesToMemory() 15 app.CopyTemplatesToMemory()
12 err := devianter.UpdateCSRF() 16
13 if err != nil { 17 go func() {
14 println(err.Error()) 18 for {
15 } 19 err := devianter.UpdateCSRF()
20 if err != nil {
21 println(err.Error())
22 }
23 time.Sleep(12 * time.Hour)
24 }
25 }()
16 26
17 app.Router() 27 app.Router()
18} 28}
services/skunkyart.example.openrc +15 −8
@@ -1,13 +1,20 @@
1#!/sbin/openrc-run 1#!/sbin/openrc-run
2
3directory=<path_to_dir_with_skunkyart>
4# command_args="-c $directory/config.json" # if SA wasn't start, try to uncomment this line
5command=$directory/skunkyart
6
7name="SkunkyArt"
8description="Privacy frontend for deviantart.com"
9supervisor=supervise-daemon 2supervisor=supervise-daemon
3user=skunkyart:skunkyart
4name=SkunkyArt
5directory=/opt/skunkyart
6command=$directory/bin/skunkyart
7description="Privacy-oriented frontend for DeviantArt"
8error_logger=logger
9output_logger=logger
10no_new_privs=true
11umask=0077
12
13# if you use old version openrc, uncomment function lower
14# start_pre() {
15# cd $directory
16# }
10 17
11depend() { 18depend() {
12 need net 19 need net localmount bootmisc
13}\ No newline at end of file 20}\ No newline at end of file
services/skunkyart.example.service +1 −1
@@ -1,7 +1,7 @@
1# Note: i didn't use systemd, so it can be not works :) 1# Note: i didn't use systemd, so it can be not works :)
2 2
3[Unit] 3[Unit]
4Description=Privacy frontend for deviantart.com 4Description=Privacy-oriented frontend for DeviantArt
5 5
6[Service] 6[Service]
7Directory=<path-to-dir-with-skunkyart> 7Directory=<path-to-dir-with-skunkyart>