mirror of
https://github.com/rr-/szurubooru.git
synced 2025-07-17 08:26:24 +00:00
Compare commits
7 Commits
cf569ed4bf
...
181f2e1828
Author | SHA1 | Date | |
---|---|---|---|
181f2e1828 | |||
376f687c38 | |||
4fd848abf2 | |||
e59beb4670 | |||
922499cb64 | |||
a88e73804c | |||
7a0a65bee4 |
@ -789,7 +789,7 @@ data.
|
|||||||
| `fav-time` | alias of `fav-date` |
|
| `fav-time` | alias of `fav-date` |
|
||||||
| `feature-date` | featured at given date |
|
| `feature-date` | featured at given date |
|
||||||
| `feature-time` | alias of `feature-time` |
|
| `feature-time` | alias of `feature-time` |
|
||||||
| `safety` | having given safety. `<value>` can be either `safe`, `sketchy` (or `questionable`) or `unsafe`. |
|
| `safety` | having given safety. `<value>` can be either `safe`, `sketchy` or `unsafe`. |
|
||||||
| `rating` | alias of `safety` |
|
| `rating` | alias of `safety` |
|
||||||
|
|
||||||
**Sort style tokens**
|
**Sort style tokens**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
This assumes that you have Docker (version 17.05 or greater)
|
This assumes that you have Docker (version 19.03 or greater)
|
||||||
and Docker Compose (version 1.6.0 or greater) already installed.
|
and the Docker Compose CLI (version 1.27.0 or greater) already installed.
|
||||||
|
|
||||||
### Prepare things
|
### Prepare things
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ and Docker Compose (version 1.6.0 or greater) already installed.
|
|||||||
|
|
||||||
This pulls the latest containers from docker.io:
|
This pulls the latest containers from docker.io:
|
||||||
```console
|
```console
|
||||||
user@host:szuru$ docker-compose pull
|
user@host:szuru$ docker compose pull
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have modified the application's source and would like to manually
|
If you have modified the application's source and would like to manually
|
||||||
@ -49,17 +49,17 @@ and Docker Compose (version 1.6.0 or greater) already installed.
|
|||||||
|
|
||||||
For first run, it is recommended to start the database separately:
|
For first run, it is recommended to start the database separately:
|
||||||
```console
|
```console
|
||||||
user@host:szuru$ docker-compose up -d sql
|
user@host:szuru$ docker compose up -d sql
|
||||||
```
|
```
|
||||||
|
|
||||||
To start all containers:
|
To start all containers:
|
||||||
```console
|
```console
|
||||||
user@host:szuru$ docker-compose up -d
|
user@host:szuru$ docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
To view/monitor the application logs:
|
To view/monitor the application logs:
|
||||||
```console
|
```console
|
||||||
user@host:szuru$ docker-compose logs -f
|
user@host:szuru$ docker compose logs -f
|
||||||
# (CTRL+C to exit)
|
# (CTRL+C to exit)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -84,13 +84,13 @@ and Docker Compose (version 1.6.0 or greater) already installed.
|
|||||||
2. Build the containers:
|
2. Build the containers:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
user@host:szuru$ docker-compose build
|
user@host:szuru$ docker compose build
|
||||||
```
|
```
|
||||||
|
|
||||||
That will attempt to build both containers, but you can specify `client`
|
That will attempt to build both containers, but you can specify `client`
|
||||||
or `server` to make it build only one.
|
or `server` to make it build only one.
|
||||||
|
|
||||||
If `docker-compose build` spits out:
|
If `docker compose build` spits out:
|
||||||
|
|
||||||
```
|
```
|
||||||
ERROR: Service 'server' failed to build: failed to parse platform : "" is an invalid component of "": platform specifier component must match "^[A-Za-z0-9_-]+$": invalid argument
|
ERROR: Service 'server' failed to build: failed to parse platform : "" is an invalid component of "": platform specifier component must match "^[A-Za-z0-9_-]+$": invalid argument
|
||||||
@ -102,7 +102,7 @@ and Docker Compose (version 1.6.0 or greater) already installed.
|
|||||||
user@host:szuru$ export DOCKER_BUILDKIT=1; export COMPOSE_DOCKER_CLI_BUILD=1
|
user@host:szuru$ export DOCKER_BUILDKIT=1; export COMPOSE_DOCKER_CLI_BUILD=1
|
||||||
```
|
```
|
||||||
|
|
||||||
...and run `docker-compose build` again.
|
...and run `docker compose build` again.
|
||||||
|
|
||||||
*Note: If your changes are not taking effect in your builds, consider building
|
*Note: If your changes are not taking effect in your builds, consider building
|
||||||
with `--no-cache`.*
|
with `--no-cache`.*
|
||||||
@ -117,7 +117,7 @@ with `--no-cache`.*
|
|||||||
run from docker:
|
run from docker:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
user@host:szuru$ docker-compose run server ./szuru-admin --help
|
user@host:szuru$ docker compose run server ./szuru-admin --help
|
||||||
```
|
```
|
||||||
|
|
||||||
will give you a breakdown on all available commands.
|
will give you a breakdown on all available commands.
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
## Example Docker Compose configuration
|
## Example Docker Compose configuration
|
||||||
##
|
##
|
||||||
## Use this as a template to set up docker-compose, or as guide to set up other
|
## Use this as a template to set up docker compose, or as guide to set up other
|
||||||
## orchestration services
|
## orchestration services
|
||||||
version: '2'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
server:
|
server:
|
||||||
|
@ -169,6 +169,9 @@ privileges:
|
|||||||
'uploads:create': regular
|
'uploads:create': regular
|
||||||
'uploads:use_downloader': power
|
'uploads:use_downloader': power
|
||||||
|
|
||||||
|
homepage_url: https://www.example.com/
|
||||||
|
site_url: https://www.example.com/booru
|
||||||
|
|
||||||
## ONLY SET THESE IF DEPLOYING OUTSIDE OF DOCKER
|
## ONLY SET THESE IF DEPLOYING OUTSIDE OF DOCKER
|
||||||
#debug: 0 # generate server logs?
|
#debug: 0 # generate server logs?
|
||||||
#show_sql: 0 # show sql in server logs?
|
#show_sql: 0 # show sql in server logs?
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import szurubooru.api.comment_api
|
import szurubooru.api.comment_api
|
||||||
|
import szurubooru.api.embed_api
|
||||||
import szurubooru.api.info_api
|
import szurubooru.api.info_api
|
||||||
import szurubooru.api.password_reset_api
|
import szurubooru.api.password_reset_api
|
||||||
import szurubooru.api.pool_api
|
import szurubooru.api.pool_api
|
||||||
|
101
server/szurubooru/api/embed_api.py
Normal file
101
server/szurubooru/api/embed_api.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import re
|
||||||
|
import html
|
||||||
|
from urllib.parse import quote
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from szurubooru import config, model, rest
|
||||||
|
from szurubooru.func import (
|
||||||
|
auth,
|
||||||
|
posts,
|
||||||
|
serialization,
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(f"{config.config['data_dir']}/../index.htm") as index:
|
||||||
|
index_html = index.read()
|
||||||
|
|
||||||
|
def _index_path(params: Dict[str, str]) -> int:
|
||||||
|
try:
|
||||||
|
return params["path"]
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
raise posts.InvalidPostIdError(
|
||||||
|
"Invalid post ID."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_post(post_id: int) -> model.Post:
|
||||||
|
return posts.get_post_by_id(post_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_post_id(match: re.Match) -> int:
|
||||||
|
post_id = match.group("post_id")
|
||||||
|
try:
|
||||||
|
return int(post_id)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
raise posts.InvalidPostIdError(
|
||||||
|
"Invalid post ID: %r." % post_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _serialize_post(
|
||||||
|
ctx: rest.Context, post: Optional[model.Post]
|
||||||
|
) -> rest.Response:
|
||||||
|
return posts.serialize_post(
|
||||||
|
post, ctx.user, options=["thumbnailUrl", "user"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@rest.routes.get("/oembed/?")
|
||||||
|
def get_post(
|
||||||
|
ctx: rest.Context, _params: Dict[str, str] = {}, url: str = ""
|
||||||
|
) -> rest.Response:
|
||||||
|
auth.verify_privilege(ctx.user, "posts:view")
|
||||||
|
|
||||||
|
url = url or ctx.get_param_as_string("url")
|
||||||
|
match = re.match(r".*?/post/(?P<post_id>\d+)", url)
|
||||||
|
if not match:
|
||||||
|
raise posts.InvalidPostIdError("Invalid post ID.")
|
||||||
|
|
||||||
|
post_id = _get_post_id(match)
|
||||||
|
post = _get_post(post_id)
|
||||||
|
serialized = _serialize_post(ctx, post)
|
||||||
|
embed = {
|
||||||
|
"version": "1.0",
|
||||||
|
"type": "photo",
|
||||||
|
"title": f"{config.config['name']} – Post #{post_id}",
|
||||||
|
"author_name": serialized["user"]["name"] if serialized["user"] else None,
|
||||||
|
"provider_name": config.config["name"],
|
||||||
|
"provider_url": config.config["homepage_url"],
|
||||||
|
"thumbnail_url": f"{config.config['site_url']}/{serialized['thumbnailUrl']}",
|
||||||
|
"thumbnail_width": int(config.config["thumbnails"]["post_width"]),
|
||||||
|
"thumbnail_height": int(config.config["thumbnails"]["post_height"]),
|
||||||
|
"url": f"{config.config['site_url']}/{serialized['thumbnailUrl']}",
|
||||||
|
"width": int(config.config["thumbnails"]["post_width"]),
|
||||||
|
"height": int(config.config["thumbnails"]["post_height"])
|
||||||
|
}
|
||||||
|
return embed
|
||||||
|
|
||||||
|
|
||||||
|
@rest.routes.get("/index(?P<path>/.+)")
|
||||||
|
def post_index(ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
|
||||||
|
path = _index_path(params)
|
||||||
|
try:
|
||||||
|
oembed = get_post(ctx, {}, path)
|
||||||
|
except posts.PostNotFoundError:
|
||||||
|
return {"return_type": "custom", "status_code": "404", "content": index_html}
|
||||||
|
|
||||||
|
url = config.config["site_url"] + path
|
||||||
|
new_html = index_html.replace("</head>", f'''
|
||||||
|
<meta property="og:site_name" content="{config.config["name"]}">
|
||||||
|
<meta property="og:url" content="{html.escape(url)}">
|
||||||
|
<meta property="og:type" content="article">
|
||||||
|
<meta property="og:title" content="{html.escape(oembed['title'])}">
|
||||||
|
<meta name="twitter:title" content="{html.escape(oembed['title'])}">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:image" content="{html.escape(oembed['url'])}">
|
||||||
|
<meta property="og:image:url" content="{html.escape(oembed['url'])}">
|
||||||
|
<meta property="og:image:width" content="{oembed['width']}">
|
||||||
|
<meta property="og:image:height" content="{oembed['height']}">
|
||||||
|
<meta property="article:author" content="{html.escape(oembed['author_name'] or '')}">
|
||||||
|
<link rel="alternate" type="application/json+oembed" href="{config.config["site_url"]}/api/oembed?url={quote(html.escape(url))}" title="{html.escape(config.config["name"])}"></head>
|
||||||
|
''').replace("<html>", '<html prefix="og: http://ogp.me/ns#">').replace("<title>Loading...</title>", f"<title>{html.escape(oembed['title'])}</title>")
|
||||||
|
return {"return_type": "custom", "content": new_html}
|
@ -74,7 +74,8 @@ def application(
|
|||||||
) -> Tuple[bytes]:
|
) -> Tuple[bytes]:
|
||||||
try:
|
try:
|
||||||
ctx = _create_context(env)
|
ctx = _create_context(env)
|
||||||
if "application/json" not in ctx.get_header("Accept"):
|
accept_header = ctx.get_header("Accept")
|
||||||
|
if "*/*" not in accept_header and "application/json" not in accept_header:
|
||||||
raise errors.HttpNotAcceptable(
|
raise errors.HttpNotAcceptable(
|
||||||
"ValidationError", "This API only supports JSON responses."
|
"ValidationError", "This API only supports JSON responses."
|
||||||
)
|
)
|
||||||
@ -111,6 +112,10 @@ def application(
|
|||||||
finally:
|
finally:
|
||||||
db.session.remove()
|
db.session.remove()
|
||||||
|
|
||||||
|
if type(response) == dict and response.get("return_type") == "custom":
|
||||||
|
start_response(response.get("status_code", "200"), [("content-type", "text/html")])
|
||||||
|
return (response.get("content", "").encode("utf-8"),)
|
||||||
|
|
||||||
start_response("200", [("content-type", "application/json")])
|
start_response("200", [("content-type", "application/json")])
|
||||||
return (_dump_json(response).encode("utf-8"),)
|
return (_dump_json(response).encode("utf-8"),)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user