Compare commits

..

2 commits

7 changed files with 87 additions and 31 deletions

View file

@ -5,12 +5,12 @@
- Switched to Quart framework. This implies everything is `async def` now.
- **BREAKING**: `SERVER_NAME` env variable now contains the domain name. `DOMAIN_NAME` has been removed.
- libsuou bumped to 0.6.0
- Added several REST routes. Change needed due to pending frontend separation.
- Added several REST routes. Change needed due to pending [frontend separation](https://nekode.yusur.moe/yusur/vigil).
- Deprecated the old web routes except for `/report` and `/admin`
## 0.4.0
- Added dependency to [SUOU](https://github.com/sakuragasaki46/suou) library
- Added dependency to [SUOU](https://github.com/yusurko/suou) library
- Users can now block each other
+ Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile
- Added user strikes: a strike logs the content of a removed message for future use

View file

@ -26,7 +26,7 @@ from suou import twocolon_list, WantsContentType
from .colors import color_themes, theme_classes
__version__ = '0.5.0-dev45'
__version__ = '0.5.0-dev47'
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@ -40,11 +40,12 @@ class AppConfig(ConfigOptions):
server_name = ConfigValue()
force_server_name = ConfigValue(cast=yesno, default=True)
private_assets = ConfigValue(cast=ssv_list)
# deprecated
jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js')
app_is_behind_proxy = ConfigValue(cast=int, default=0)
impressum = ConfigValue(cast=twocolon_list, default='')
create_guild_threshold = ConfigValue(cast=int, default=15, prefix='freak_')
# v-- deprecated --v
jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js')
# ^----------------^
app_config = AppConfig()

View file

@ -1,12 +1,16 @@
from __future__ import annotations
import datetime
import sys
from typing import Iterable, TypeVar
import logging
from quart import render_template, session
from quart import abort, Blueprint, redirect, request, url_for
from pydantic import BaseModel
from pydantic import BaseModel, Field
from quart_auth import current_user, login_required, login_user, logout_user
from quart_schema import validate_request, validate_response
from quart_wtf.csrf import generate_csrf
from sqlalchemy import delete, insert, select
from suou import Snowflake, deprecated, makelist, not_implemented, want_isodate
@ -21,6 +25,7 @@ from freak.search import SearchQuery
from ..models import Comment, Guild, Post, PostUpvote, User, db
from .. import UserLoader, app, app_config, __version__ as freak_version, csrf
logger = logging.getLogger(__name__)
_T = TypeVar('_T')
bp = Blueprint('rest', __name__, url_prefix='/v1')
@ -64,8 +69,13 @@ async def oath():
## pull csrf token from session
csrf_tok = session['csrf_token']
except Exception as e:
print(e)
abort(503, "csrf_token is null")
try:
logger.warning('CSRF token regenerated!')
csrf_tok = session['csrf_token'] = generate_csrf()
except Exception as e2:
print(e, e2)
abort(503, "csrf_token is null")
return dict(
## XXX might break any time!
csrf_token= csrf_tok
@ -266,6 +276,44 @@ async def guild_feed(gname: str):
return dict(guilds={f'{Snowflake(gu.id):l}': gj}, feed=feed)
## CREATE ##
class CreateIn(BaseModel):
title: str
content: str
privacy: int = Field(default=0, ge=0, lt=4)
@bp.post('/guild/@<gname>')
@login_required
@validate_request(CreateIn)
async def guild_post(data: CreateIn, gname: str):
async with db as session:
user = current_user.user
gu: Guild | None = (await session.execute(select(Guild).where(Guild.name == gname))).scalar()
if gu is None:
return dict(error='Not found'), 404
if await gu.has_exiled(current_user.user):
return dict(error=f'You are banned from +{gname}'), 403
if not await gu.allows_posting(current_user.user):
return dict(error=f'You can\'t post on +{gname}'), 403
try:
new_post_id: int = (await session.execute(insert(Post).values(
author_id = user.id,
topic_id = gu.id,
privacy = data.privacy,
title = data.title,
text_content = data.text
).returning(Post.id))).scalar()
session.commit()
return dict(id=Snowflake(new_post_id).to_b32l()), 200
except Exception:
sys.excepthook(*sys.exc_info())
return {'error': 'Internal Server Error'}, 500
## LOGIN/OUT ##
class LoginIn(BaseModel):

View file

@ -13,6 +13,9 @@
<a href="?reason={{ opt.code |urlencode }}">{{ opt.description }}</a>
</li>
{% endfor %}
<li class="faint">
<a href="{{ back_to_url or 'javascript:history.go(-1);' }}">I clicked "Report" by mistake</a>
</li>
</ul>
{% endblock %}

View file

@ -13,6 +13,9 @@
<a href="?reason={{ opt.code |urlencode }}">{{ opt.description }}</a>
</li>
{% endfor %}
<li class="faint">
<a href="{{ back_to_url or 'javascript:history.go(-1);' }}">I clicked "Report" by mistake</a>
</li>
</ul>
{% endblock %}

View file

@ -1,19 +1,19 @@
# Terms of Service
This is a non-authoritative copy of the actual Terms, always updated at <https://yusur.moe/policies/terms.html>.
This is a non-authoritative copy of the actual Terms, always updated at <https://ndspir.it/terms.html>.
The following documents are incorporated into these Terms by reference
(i.e. an extension to these Terms in force):
* [Privacy Policy](/privacy)
* [Community Guidelines](/rules)
* [User Generated Content Terms](https://yusur.moe/policies/ugc.html) on newdigitalspirit.com
* [Minors' Account Policy](https://yusur.moe/policies/u18.html) on newdigitalspirit.com
* [User Generated Content Terms](https://ndspir.it/ugc.html) on newdigitalspirit.com
* [Minors' Account Policy](https://ndspir.it/u18.html) on newdigitalspirit.com
## Scope and Definition
These terms of service ("Terms") are between **New Digital Spirit** and You,
These terms of service ("Terms") are between **{{ app_name }}** and You,
regarding Your use of all sites and services belonging to New Digital Spirit ("New Digital Spirit Network" / "the Services"),
listed in detail in [Privacy Policy](/policies/privacy.html).
@ -21,27 +21,27 @@ Other websites are not covered by these Terms.
## Age
The whole of New Digital Spirit Network is PG-13. You may not use the Services if you are younger than 13 years old.
The whole of {{ app_name }} is PG-13. You may not use the Services if you are younger than 13 years old.
Additionally, you may not directly contact New Digital Spirit if you are younger than 18 years old, for any reason besides
Additionally, you may not directly contact {{ app_name }} if you are younger than 18 years old, for any reason besides
privacy-related requests. Any contact request knowingly from people younger than 18 will be ignored.
United States resident under the age of 18 are **not allowed** in any way to access our network without logging in.
New Digital Spirit reserves the right to require ID verification in case of age doubt or potential security threat.
New Digital Spirit reserves the right to require ID verification in case of age doubt or suspected security threat.
Minors on New Digital Spirit Network are additionally bound to the [Minor Account Policy](/policies/u18.html),
Minors on New Digital Spirit Network are additionally bound to the [Minor Account Policy](https://ndspir.it/u18.html),
incorporated here by reference.
Systems and plurals are considered to be minors, no matter their body age.
## Intellectual property
Except otherwise noted, the entirety of the content on the New Digital Spirit Network
is intellectual property of Sakuragasaki46 and New Digital Spirit. All rights reserved.
Except otherwise noted, the entirety of the content on {{ app_name }}
is intellectual property of {{ app_name }}. All rights reserved.
You may not copy, modify, redistribute, mirror the contents of or create alternative Service to
yusur.moe or any other of the Services, or portions thereof, without New Digital Spirit's
{{ server_name }} or any other of the Services, or portions thereof, without {{ app_name }}'s
prior written permission.
## Privacy Rights
@ -53,7 +53,7 @@ identification or damages to Sakuragasaki46's private life.
Disclosure will be legally regarded as a violation of privacy and a breach of
non-disclosure agreement (NDA), and will be acted upon accordingly, regardless of
the infringer's age or any other legal protection, included but not limited to
termination of the infringer,s accounts.
termination of the infringer's accounts.
## IP Loggers
@ -65,11 +65,11 @@ legitimate interest. Logged information contains user agent strings as well.
## User Generated Content
Some of our Services allow user generated content. By using them, you agree to be bound
to the [User Generated Content Terms](/policies/ugc.html), incorporated here by reference.
to the [User Generated Content Terms](https://ndspir.it/ugc.html), incorporated here by reference.
## No Warranty
**Except as represented in this agreement, the New Digital Spirit Network
**Except as represented in this agreement, {{ app_name }}
is provided “AS IS”. Other than as provided in this agreement,
New Digital Spirit makes no other warranties, express or implied, and hereby
disclaims all implied warranties, including any warranty of merchantability
@ -77,13 +77,13 @@ and warranty of fitness for a particular purpose.**
## Liability
Sakuragasaki46 or New Digital Spirit **shall not be accountable** for Your damages arising from Your use
{{ app_name }} **shall not be accountable** for Your damages arising from Your use
of the New Digital Spirit Network.
## Indemnify
You agree to [indemnify and hold harmless](https://www.upcounsel.com/difference-between-indemnify-and-hold-harmless)
Sakuragasaki46 and New Digital Spirit from any and all claims, damages, liabilities, costs and expenses, including reasonable and unreasonable
{{ app_name }} from any and all claims, damages, liabilities, costs and expenses, including reasonable and unreasonable
counsel and attorneys fees, arising out of any breach of this agreement.
## Severability
@ -95,7 +95,7 @@ according to the governing law, the remainder of these Terms shall remain in pla
These terms of services are governed by, and shall be interpreted in accordance
with, the laws of Italy. You consent to the sole jurisdiction of \[REDACTED], Italy
for all disputes between You and , and You consent to the sole
for all disputes between You and {{ app_name }}, and You consent to the sole
application of Italian law and European Union law for all such disputes.
## Updates

View file

@ -103,7 +103,8 @@ async def accept_report(target, source: PostReport):
await remove_content(target, source.reason_code)
source.update_status = REPORT_UPDATE_COMPLETE
session.add(source)
# XXX disabled because of a session conflict
#session.add(source)
@additem(REPORT_ACTIONS, '2')
@ -127,21 +128,21 @@ async def strike_report(target, source: PostReport):
author.banned_reason = source.reason_code
source.update_status = REPORT_UPDATE_COMPLETE
session.add(source)
#session.add(source)
@additem(REPORT_ACTIONS, '0')
async def reject_report(target, source: PostReport):
async with db as session:
source.update_status = REPORT_UPDATE_REJECTED
session.add(source)
#session.add(source)
@additem(REPORT_ACTIONS, '3')
async def withhold_report(target, source: PostReport):
async with db as session:
source.update_status = REPORT_UPDATE_ON_HOLD
session.add(source)
#session.add(source)
@additem(REPORT_ACTIONS, '4')
@ -196,7 +197,7 @@ async def strikes():
@bp.route('/admin/users/')
@admin_required
async def users():
user_list = await db.paginate(select(User).order_by(User.joined_at.desc()))
user_list = await db.paginate(select(User).order_by(User.joined_at.desc()), page=int(request.args.get('page', 1)))
return await render_template('admin/admin_users.html',
user_list=user_list, account_status_string=colorized_account_status_string)
@ -204,7 +205,7 @@ async def users():
@admin_required
async def user_detail(id: int):
async with db as session:
u = session.execute(select(User).where(User.id == id)).scalar()
u = (await session.execute(select(User).where(User.id == id))).scalar()
if u is None:
abort(404)
if request.method == 'POST':