feat: added title and description to api and db
This commit is contained in:
parent
61b9f81e39
commit
a4f0101956
11 changed files with 144 additions and 9 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,3 +13,5 @@ server/**/lib/
|
||||||
server/**/bin/
|
server/**/bin/
|
||||||
server/**/pyvenv.cfg
|
server/**/pyvenv.cfg
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|
||||||
|
tmp
|
0
Makefile
Normal file
0
Makefile
Normal file
|
@ -169,6 +169,12 @@ class PostMainController extends BasePostController {
|
||||||
this._view.sidebarControl.disableForm();
|
this._view.sidebarControl.disableForm();
|
||||||
this._view.sidebarControl.clearMessages();
|
this._view.sidebarControl.clearMessages();
|
||||||
const post = e.detail.post;
|
const post = e.detail.post;
|
||||||
|
if (e.detail.title !== undefined && e.detail.title !== null) {
|
||||||
|
post.title = e.detail.title;
|
||||||
|
}
|
||||||
|
if (e.detail.description !== undefined && e.detail.description !== null) {
|
||||||
|
post.description = e.detail.description;
|
||||||
|
}
|
||||||
if (e.detail.safety !== undefined && e.detail.safety !== null) {
|
if (e.detail.safety !== undefined && e.detail.safety !== null) {
|
||||||
post.safety = e.detail.safety;
|
post.safety = e.detail.safety;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,14 @@ class Post extends events.EventTarget {
|
||||||
return this._user;
|
return this._user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this._title;
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
return this._description;
|
||||||
|
}
|
||||||
|
|
||||||
get safety() {
|
get safety() {
|
||||||
return this._safety;
|
return this._safety;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +258,12 @@ class Post extends events.EventTarget {
|
||||||
if (anonymous === true) {
|
if (anonymous === true) {
|
||||||
detail.anonymous = true;
|
detail.anonymous = true;
|
||||||
}
|
}
|
||||||
|
if (this._title !== this._orig._title) {
|
||||||
|
detail.title = this._title;
|
||||||
|
}
|
||||||
|
if (this._description !== this._orig._description) {
|
||||||
|
detail.description = this._description;
|
||||||
|
}
|
||||||
if (this._safety !== this._orig._safety) {
|
if (this._safety !== this._orig._safety) {
|
||||||
detail.safety = this._safety;
|
detail.safety = this._safety;
|
||||||
}
|
}
|
||||||
|
@ -471,6 +485,8 @@ class Post extends events.EventTarget {
|
||||||
_checksumMD5: response.checksumMD5,
|
_checksumMD5: response.checksumMD5,
|
||||||
_creationTime: response.creationTime,
|
_creationTime: response.creationTime,
|
||||||
_user: response.user,
|
_user: response.user,
|
||||||
|
_title: response.title,
|
||||||
|
_description: response.description,
|
||||||
_safety: response.safety,
|
_safety: response.safety,
|
||||||
_contentUrl: response.contentUrl,
|
_contentUrl: response.contentUrl,
|
||||||
_fullContentUrl: new URL(
|
_fullContentUrl: new URL(
|
||||||
|
|
|
@ -10,15 +10,16 @@ fi
|
||||||
# Create a dummy container
|
# Create a dummy container
|
||||||
WORKDIR="$(git rev-parse --show-toplevel)/server"
|
WORKDIR="$(git rev-parse --show-toplevel)/server"
|
||||||
IMAGE=$(docker build -q "${WORKDIR}")
|
IMAGE=$(docker build -q "${WORKDIR}")
|
||||||
CONTAINER=$(docker run -d ${IMAGE} tail -f /dev/null)
|
CONTAINER=$(docker run --network=host -d ${IMAGE} tail -f /dev/null)
|
||||||
|
|
||||||
# Create the migration script
|
# Create the migration script
|
||||||
docker exec -i \
|
docker exec -i \
|
||||||
-e PYTHONPATH='/opt/app' \
|
-e PYTHONPATH='/opt/app' \
|
||||||
-e POSTGRES_HOST='x' \
|
-e POSTGRES_HOST='localhost' \
|
||||||
-e POSTGRES_USER='x' \
|
-e POSTGRES_PORT='15432' \
|
||||||
-e POSTGRES_PASSWORD='x' \
|
-e POSTGRES_USER='szuru' \
|
||||||
${CONTAINER} alembic revision -m "$1"
|
-e POSTGRES_PASSWORD='changeme' \
|
||||||
|
${CONTAINER} alembic revision --autogenerate -m "$1"
|
||||||
|
|
||||||
# Copy the file over from the container
|
# Copy the file over from the container
|
||||||
docker cp ${CONTAINER}:/opt/app/szurubooru/migrations/versions/ \
|
docker cp ${CONTAINER}:/opt/app/szurubooru/migrations/versions/ \
|
||||||
|
|
48
docker-compose.local.yml
Normal file
48
docker-compose.local.yml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
## Example Docker Compose configuration
|
||||||
|
##
|
||||||
|
## Use this as a template to set up docker-compose, or as guide to set up other
|
||||||
|
## orchestration services
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
server:
|
||||||
|
image: szurubooru/server:latest
|
||||||
|
depends_on:
|
||||||
|
- sql
|
||||||
|
environment:
|
||||||
|
## These should be the names of the dependent containers listed below,
|
||||||
|
## or FQDNs/IP addresses if these services are running outside of Docker
|
||||||
|
POSTGRES_HOST: sql
|
||||||
|
## Credentials for database:
|
||||||
|
POSTGRES_USER:
|
||||||
|
POSTGRES_PASSWORD:
|
||||||
|
## Commented Values are Default:
|
||||||
|
#POSTGRES_DB: defaults to same as POSTGRES_USER
|
||||||
|
#POSTGRES_PORT: 5432
|
||||||
|
#LOG_SQL: 0 (1 for verbose SQL logs)
|
||||||
|
THREADS:
|
||||||
|
volumes:
|
||||||
|
- "${MOUNT_DATA}:/data"
|
||||||
|
- "./server/config.yaml:/opt/app/config.yaml"
|
||||||
|
|
||||||
|
client:
|
||||||
|
image: szurubooru/client:latest
|
||||||
|
depends_on:
|
||||||
|
- server
|
||||||
|
environment:
|
||||||
|
BACKEND_HOST: server
|
||||||
|
BASE_URL:
|
||||||
|
volumes:
|
||||||
|
- "${MOUNT_DATA}:/data:ro"
|
||||||
|
ports:
|
||||||
|
- "${PORT}:80"
|
||||||
|
|
||||||
|
sql:
|
||||||
|
image: postgres:11-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: szuru
|
||||||
|
POSTGRES_PASSWORD: changeme
|
||||||
|
volumes:
|
||||||
|
- "${MOUNT_SQL}:/var/lib/postgresql/data"
|
|
@ -2,8 +2,6 @@
|
||||||
##
|
##
|
||||||
## 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:
|
||||||
|
@ -15,14 +13,18 @@ services:
|
||||||
## or FQDNs/IP addresses if these services are running outside of Docker
|
## or FQDNs/IP addresses if these services are running outside of Docker
|
||||||
POSTGRES_HOST: sql
|
POSTGRES_HOST: sql
|
||||||
## Credentials for database:
|
## Credentials for database:
|
||||||
POSTGRES_USER:
|
POSTGRES_USER: szuru
|
||||||
POSTGRES_PASSWORD:
|
POSTGRES_PASSWORD: changeme
|
||||||
## Commented Values are Default:
|
## Commented Values are Default:
|
||||||
#POSTGRES_DB: defaults to same as POSTGRES_USER
|
#POSTGRES_DB: defaults to same as POSTGRES_USER
|
||||||
#POSTGRES_PORT: 5432
|
#POSTGRES_PORT: 5432
|
||||||
#LOG_SQL: 0 (1 for verbose SQL logs)
|
#LOG_SQL: 0 (1 for verbose SQL logs)
|
||||||
THREADS:
|
THREADS:
|
||||||
|
UID: ${UID}
|
||||||
|
GID: ${GID}
|
||||||
|
user: "${UID}:${GID}"
|
||||||
volumes:
|
volumes:
|
||||||
|
- "./server:/opt/app"
|
||||||
- "${MOUNT_DATA}:/data"
|
- "${MOUNT_DATA}:/data"
|
||||||
- "./server/config.yaml:/opt/app/config.yaml"
|
- "./server/config.yaml:/opt/app/config.yaml"
|
||||||
|
|
||||||
|
@ -44,5 +46,7 @@ services:
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER:
|
POSTGRES_USER:
|
||||||
POSTGRES_PASSWORD:
|
POSTGRES_PASSWORD:
|
||||||
|
ports:
|
||||||
|
- 15432:5432
|
||||||
volumes:
|
volumes:
|
||||||
- "${MOUNT_SQL}:/var/lib/postgresql/data"
|
- "${MOUNT_SQL}:/var/lib/postgresql/data"
|
||||||
|
|
|
@ -65,6 +65,14 @@ def create_post(
|
||||||
ctx.user, "uploads:use_downloader"
|
ctx.user, "uploads:use_downloader"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
title = ""
|
||||||
|
if ctx.has_param("title"):
|
||||||
|
title = ctx.get_param_as_string("title")
|
||||||
|
|
||||||
|
description = ""
|
||||||
|
if ctx.has_param("description"):
|
||||||
|
description = ctx.get_param_as_string("description")
|
||||||
|
|
||||||
tag_names = ctx.get_param_as_string_list("tags", default=[])
|
tag_names = ctx.get_param_as_string_list("tags", default=[])
|
||||||
safety = ctx.get_param_as_string("safety")
|
safety = ctx.get_param_as_string("safety")
|
||||||
source = ctx.get_param_as_string("source", default="")
|
source = ctx.get_param_as_string("source", default="")
|
||||||
|
@ -81,6 +89,8 @@ def create_post(
|
||||||
)
|
)
|
||||||
if len(new_tags):
|
if len(new_tags):
|
||||||
auth.verify_privilege(ctx.user, "tags:create")
|
auth.verify_privilege(ctx.user, "tags:create")
|
||||||
|
posts.update_post_title(post, title)
|
||||||
|
posts.update_post_description(post, description)
|
||||||
posts.update_post_safety(post, safety)
|
posts.update_post_safety(post, safety)
|
||||||
posts.update_post_source(post, source)
|
posts.update_post_source(post, source)
|
||||||
posts.update_post_relations(post, relations)
|
posts.update_post_relations(post, relations)
|
||||||
|
@ -143,6 +153,14 @@ def update_post(ctx: rest.Context, params: Dict[str, str]) -> rest.Response:
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
for tag in new_tags:
|
for tag in new_tags:
|
||||||
snapshots.create(tag, ctx.user)
|
snapshots.create(tag, ctx.user)
|
||||||
|
if ctx.has_param("title"):
|
||||||
|
# for now we're just treating this fields as "content"
|
||||||
|
auth.verify_privilege(ctx.user, "posts:edit:content")
|
||||||
|
posts.update_post_title(post, ctx.get_param_as_string("title"))
|
||||||
|
if ctx.has_param("description"):
|
||||||
|
# for now we're just treating this fields as "content"
|
||||||
|
auth.verify_privilege(ctx.user, "posts:edit:content")
|
||||||
|
posts.update_post_description(post, ctx.get_param_as_string("description"))
|
||||||
if ctx.has_param("safety"):
|
if ctx.has_param("safety"):
|
||||||
auth.verify_privilege(ctx.user, "posts:edit:safety")
|
auth.verify_privilege(ctx.user, "posts:edit:safety")
|
||||||
posts.update_post_safety(post, ctx.get_param_as_string("safety"))
|
posts.update_post_safety(post, ctx.get_param_as_string("safety"))
|
||||||
|
|
|
@ -414,6 +414,8 @@ def create_post(
|
||||||
post.creation_time = datetime.utcnow()
|
post.creation_time = datetime.utcnow()
|
||||||
post.flags = []
|
post.flags = []
|
||||||
|
|
||||||
|
post.title = ""
|
||||||
|
post.description = ""
|
||||||
post.type = ""
|
post.type = ""
|
||||||
post.checksum = ""
|
post.checksum = ""
|
||||||
post.mime_type = ""
|
post.mime_type = ""
|
||||||
|
@ -441,6 +443,18 @@ def update_post_source(post: model.Post, source: Optional[str]) -> None:
|
||||||
raise InvalidPostSourceError("Source is too long.")
|
raise InvalidPostSourceError("Source is too long.")
|
||||||
post.source = source or None
|
post.source = source or None
|
||||||
|
|
||||||
|
def update_post_title(post: model.Post, title: str) -> None:
|
||||||
|
assert post
|
||||||
|
if util.value_exceeds_column_size(title, model.Post.title):
|
||||||
|
raise InvalidPostSourceError("Title is too long.")
|
||||||
|
post.title = title
|
||||||
|
|
||||||
|
def update_post_description(post: model.Post, description: str) -> None:
|
||||||
|
assert post
|
||||||
|
if util.value_exceeds_column_size(description, model.Post.description):
|
||||||
|
raise InvalidPostSourceError("Description is too long.")
|
||||||
|
post.description = description
|
||||||
|
|
||||||
|
|
||||||
@sa.events.event.listens_for(model.Post, "after_insert")
|
@sa.events.event.listens_for(model.Post, "after_insert")
|
||||||
def _after_post_insert(
|
def _after_post_insert(
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
'''
|
||||||
|
Add title and description to posts
|
||||||
|
|
||||||
|
Revision ID: 0c149800a728
|
||||||
|
Created at: 2024-12-03 08:21:21.113161
|
||||||
|
'''
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
revision = '0c149800a728'
|
||||||
|
down_revision = 'adcd63ff76a2'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('post', sa.Column('title', sa.Unicode(length=512), nullable=False))
|
||||||
|
op.add_column('post', sa.Column('description', sa.Unicode(length=2048), nullable=False))
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('post', 'description')
|
||||||
|
op.drop_column('post', 'title')
|
|
@ -215,6 +215,8 @@ class Post(Base):
|
||||||
flags_string = sa.Column("flags", sa.Unicode(32), default="")
|
flags_string = sa.Column("flags", sa.Unicode(32), default="")
|
||||||
|
|
||||||
# content description
|
# content description
|
||||||
|
title = sa.Column("title", sa.Unicode(512), nullable=False, default="")
|
||||||
|
description = sa.Column("description", sa.Unicode(2048), nullable=False, default="")
|
||||||
type = sa.Column("type", sa.Unicode(32), nullable=False)
|
type = sa.Column("type", sa.Unicode(32), nullable=False)
|
||||||
checksum = sa.Column("checksum", sa.Unicode(64), nullable=False)
|
checksum = sa.Column("checksum", sa.Unicode(64), nullable=False)
|
||||||
checksum_md5 = sa.Column("checksum_md5", sa.Unicode(32))
|
checksum_md5 = sa.Column("checksum_md5", sa.Unicode(32))
|
||||||
|
|
Reference in a new issue