forked from jmug/stoatchat
Compare commits
41
Commits
20230120-2
...
20230311-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c8ea31d98 | ||
|
|
92ac86a6bd | ||
|
|
13ed69c82a | ||
|
|
b83f6da648 | ||
|
|
89f1167239 | ||
|
|
82d868751f | ||
|
|
31b9f18921 | ||
|
|
fd80823910 | ||
|
|
6bd8221eda | ||
|
|
ce77e926a5 | ||
|
|
2710edb76b | ||
|
|
8f1ff9e774 | ||
|
|
c09244039e | ||
|
|
bf9108408e | ||
|
|
304336d905 | ||
|
|
42b4906594 | ||
|
|
3038fb230f | ||
|
|
73af105feb | ||
|
|
2de37b23c1 | ||
|
|
0c75d8c8b3 | ||
|
|
be793b1570 | ||
|
|
5a3a97df34 | ||
|
|
0c072b01d7 | ||
|
|
056c0380b2 | ||
|
|
49598daf70 | ||
|
|
0445181d5b | ||
|
|
e780178cc0 | ||
|
|
c4b9884825 | ||
|
|
fdab997d17 | ||
|
|
8e0addc3e0 | ||
|
|
0da4baf777 | ||
|
|
c03fcda85b | ||
|
|
0afbcc065f | ||
|
|
a406e5504d | ||
|
|
e8c233f60b | ||
|
|
72595f30d4 | ||
|
|
39e345af74 | ||
|
|
e0b918771d | ||
|
|
7a6bd70dcd | ||
|
|
c0ef3d295a | ||
|
|
d74104fae4 |
@@ -1,2 +0,0 @@
|
|||||||
ko_fi: insertish
|
|
||||||
custom: https://insrt.uk/donate
|
|
||||||
@@ -15,10 +15,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install latest nightly
|
- name: Install latest stable
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repository: revoltchat/api
|
repository: revoltchat/api
|
||||||
path: api
|
path: api
|
||||||
|
token: ${{ secrets.PAT }}
|
||||||
|
|
||||||
- name: Download OpenAPI specification
|
- name: Download OpenAPI specification
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
@@ -70,6 +71,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cwd: "api"
|
cwd: "api"
|
||||||
add: "*.json"
|
add: "*.json"
|
||||||
|
author_name: Revolt CI
|
||||||
|
author_email: revolt-ci@users.noreply.github.com
|
||||||
message: "chore: generate OpenAPI specification"
|
message: "chore: generate OpenAPI specification"
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|||||||
@@ -15,22 +15,27 @@ jobs:
|
|||||||
gh api graphql -f query='
|
gh api graphql -f query='
|
||||||
query {
|
query {
|
||||||
organization(login: "revoltchat"){
|
organization(login: "revoltchat"){
|
||||||
projectNext(number: 3) {
|
projectV2(number: 3) {
|
||||||
id
|
id
|
||||||
fields(first:20) {
|
fields(first:20) {
|
||||||
nodes {
|
nodes {
|
||||||
id
|
... on ProjectV2SingleSelectField {
|
||||||
name
|
id
|
||||||
settings
|
name
|
||||||
|
options {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' > project_data.json
|
}' > project_data.json
|
||||||
|
|
||||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
|
||||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||||
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
|
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Add issue to project
|
- name: Add issue to project
|
||||||
env:
|
env:
|
||||||
@@ -39,11 +44,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
item_id="$( gh api graphql -f query='
|
item_id="$( gh api graphql -f query='
|
||||||
mutation($project:ID!, $issue:ID!) {
|
mutation($project:ID!, $issue:ID!) {
|
||||||
addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
|
addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
|
||||||
projectNextItem {
|
item {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectV2ItemById.item.id')"
|
||||||
|
|
||||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||||
|
|||||||
@@ -15,22 +15,27 @@ jobs:
|
|||||||
gh api graphql -f query='
|
gh api graphql -f query='
|
||||||
query {
|
query {
|
||||||
organization(login: "revoltchat"){
|
organization(login: "revoltchat"){
|
||||||
projectNext(number: 3) {
|
projectV2(number: 3) {
|
||||||
id
|
id
|
||||||
fields(first:20) {
|
fields(first:20) {
|
||||||
nodes {
|
nodes {
|
||||||
id
|
... on ProjectV2SingleSelectField {
|
||||||
name
|
id
|
||||||
settings
|
name
|
||||||
|
options {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' > project_data.json
|
}' > project_data.json
|
||||||
|
|
||||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
|
||||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||||
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
|
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Add PR to project
|
- name: Add PR to project
|
||||||
env:
|
env:
|
||||||
@@ -39,13 +44,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
item_id="$( gh api graphql -f query='
|
item_id="$( gh api graphql -f query='
|
||||||
mutation($project:ID!, $pr:ID!) {
|
mutation($project:ID!, $pr:ID!) {
|
||||||
addProjectNextItem(input: {projectId: $project, contentId: $pr}) {
|
addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
|
||||||
projectNextItem {
|
item {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectV2ItemById.item.id')"
|
||||||
|
|
||||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set fields
|
- name: Set fields
|
||||||
@@ -59,14 +64,16 @@ jobs:
|
|||||||
$status_field: ID!
|
$status_field: ID!
|
||||||
$status_value: String!
|
$status_value: String!
|
||||||
) {
|
) {
|
||||||
set_status: updateProjectNextItemField(input: {
|
set_status: updateProjectV2ItemFieldValue(input: {
|
||||||
projectId: $project
|
projectId: $project
|
||||||
itemId: $item
|
itemId: $item
|
||||||
fieldId: $status_field
|
fieldId: $status_field
|
||||||
value: $status_value
|
value: {
|
||||||
|
singleSelectOptionId: $status_value
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
projectNextItem {
|
projectV2Item {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent
|
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent
|
||||||
|
|||||||
Generated
+105
-101
@@ -106,7 +106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
|
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -172,11 +172,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-lock"
|
name = "async-lock"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
|
checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"event-listener",
|
"event-listener",
|
||||||
|
"futures-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -204,7 +205,7 @@ checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -269,7 +270,7 @@ checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -286,7 +287,7 @@ checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -329,6 +330,40 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "authifier"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a84411393a4326726ead660991ce8047f1865fa89900fb6c18e1e6c513cf1f9b"
|
||||||
|
dependencies = [
|
||||||
|
"async-std",
|
||||||
|
"async-trait",
|
||||||
|
"base32",
|
||||||
|
"bson",
|
||||||
|
"chrono",
|
||||||
|
"futures",
|
||||||
|
"handlebars",
|
||||||
|
"iso8601-timestamp",
|
||||||
|
"lazy_static",
|
||||||
|
"lettre",
|
||||||
|
"log",
|
||||||
|
"mongodb",
|
||||||
|
"nanoid",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"revolt_okapi",
|
||||||
|
"revolt_rocket_okapi",
|
||||||
|
"rocket",
|
||||||
|
"rust-argon2",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"totp-lite",
|
||||||
|
"ulid 0.5.0",
|
||||||
|
"validator 0.15.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@@ -681,7 +716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
|
checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -714,7 +749,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -725,7 +760,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -774,7 +809,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -807,7 +842,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"proc-macro2-diagnostics",
|
"proc-macro2-diagnostics",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -881,7 +916,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -901,7 +936,7 @@ checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1085,7 +1120,7 @@ checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1176,7 +1211,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2057,7 +2092,7 @@ dependencies = [
|
|||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2110,7 +2145,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2135,7 +2170,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "optional_struct"
|
name = "optional_struct"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/insertish/OptionalStruct?rev=e275d2726595474632485934aa0887fa52281f70#e275d2726595474632485934aa0887fa52281f70"
|
source = "git+https://github.com/insertish/OptionalStruct?rev=ee56427cee1f007839825d93d07fffd5a5e038c7#ee56427cee1f007839825d93d07fffd5a5e038c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 0.3.15",
|
"quote 0.3.15",
|
||||||
"syn 0.11.11",
|
"syn 0.11.11",
|
||||||
@@ -2215,7 +2250,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"proc-macro2-diagnostics",
|
"proc-macro2-diagnostics",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2253,7 +2288,7 @@ dependencies = [
|
|||||||
"pest_meta",
|
"pest_meta",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2284,7 +2319,7 @@ checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2371,7 +2406,7 @@ dependencies = [
|
|||||||
"proc-macro-error-attr",
|
"proc-macro-error-attr",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2388,9 +2423,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.39"
|
version = "1.0.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -2403,7 +2438,7 @@ checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
"version_check",
|
"version_check",
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
@@ -2578,39 +2613,6 @@ dependencies = [
|
|||||||
"rand_core 0.3.1",
|
"rand_core 0.3.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rauth"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "git+https://github.com/insertish/rauth?tag=1.0.3#a62eae5076d94730d06e76d438cbca0d70161856"
|
|
||||||
dependencies = [
|
|
||||||
"async-std",
|
|
||||||
"async-trait",
|
|
||||||
"base32",
|
|
||||||
"bson",
|
|
||||||
"chrono",
|
|
||||||
"futures",
|
|
||||||
"handlebars",
|
|
||||||
"iso8601-timestamp",
|
|
||||||
"lazy_static",
|
|
||||||
"lettre",
|
|
||||||
"log",
|
|
||||||
"mongodb",
|
|
||||||
"nanoid",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"regex",
|
|
||||||
"reqwest",
|
|
||||||
"revolt_okapi",
|
|
||||||
"revolt_rocket_okapi",
|
|
||||||
"rocket",
|
|
||||||
"rust-argon2",
|
|
||||||
"schemars",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"totp-lite",
|
|
||||||
"ulid 0.5.0",
|
|
||||||
"validator 0.15.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rdrand"
|
name = "rdrand"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -2704,7 +2706,7 @@ checksum = "a043824e29c94169374ac5183ac0ed43f5724dc4556b19568007486bd840fa1f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2790,7 +2792,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.6"
|
version = "0.5.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
@@ -2806,7 +2808,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.6"
|
version = "0.5.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -2833,8 +2835,8 @@ dependencies = [
|
|||||||
"revolt-quark",
|
"revolt-quark",
|
||||||
"revolt_rocket_okapi",
|
"revolt_rocket_okapi",
|
||||||
"rocket",
|
"rocket",
|
||||||
|
"rocket_authifier",
|
||||||
"rocket_empty",
|
"rocket_empty",
|
||||||
"rocket_rauth",
|
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2846,11 +2848,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.6"
|
version = "0.5.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"authifier",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bincode",
|
"bincode",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
@@ -2872,7 +2876,6 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"optional_struct",
|
"optional_struct",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rauth",
|
|
||||||
"redis-kiss",
|
"redis-kiss",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@@ -2929,7 +2932,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"rocket_http",
|
"rocket_http",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3008,6 +3011,22 @@ dependencies = [
|
|||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rocket_authifier"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4554a81937341d8c3cd3e43431ac2d23624444dc3a88e7ffd3ff66593779293"
|
||||||
|
dependencies = [
|
||||||
|
"authifier",
|
||||||
|
"iso8601-timestamp",
|
||||||
|
"revolt_okapi",
|
||||||
|
"revolt_rocket_okapi",
|
||||||
|
"rocket",
|
||||||
|
"rocket_empty",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_codegen"
|
name = "rocket_codegen"
|
||||||
version = "0.5.0-rc.2"
|
version = "0.5.0-rc.2"
|
||||||
@@ -3020,7 +3039,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"rocket_http",
|
"rocket_http",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
"unicode-xid 0.2.3",
|
"unicode-xid 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3078,21 +3097,6 @@ dependencies = [
|
|||||||
"uncased",
|
"uncased",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rocket_rauth"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "git+https://github.com/insertish/rauth?tag=1.0.3#a62eae5076d94730d06e76d438cbca0d70161856"
|
|
||||||
dependencies = [
|
|
||||||
"iso8601-timestamp",
|
|
||||||
"rauth",
|
|
||||||
"revolt_okapi",
|
|
||||||
"revolt_rocket_okapi",
|
|
||||||
"rocket",
|
|
||||||
"rocket_empty",
|
|
||||||
"schemars",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-argon2"
|
name = "rust-argon2"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -3210,7 +3214,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"serde_derive_internals",
|
"serde_derive_internals",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3361,9 +3365,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.137"
|
version = "1.0.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@@ -3379,13 +3383,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.137"
|
version = "1.0.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3396,14 +3400,14 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.81"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa 1.0.2",
|
"itoa 1.0.2",
|
||||||
@@ -3442,7 +3446,7 @@ dependencies = [
|
|||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3620,9 +3624,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.96"
|
version = "1.0.107"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
@@ -3698,7 +3702,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3807,7 +3811,7 @@ checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3943,7 +3947,7 @@ checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4073,7 +4077,7 @@ checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4284,7 +4288,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"regex",
|
"regex",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
"validator_types",
|
"validator_types",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4295,7 +4299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ded9d97e1d42327632f5f3bae6403c04886e2de3036261ef42deebd931a6a291"
|
checksum = "ded9d97e1d42327632f5f3bae6403c04886e2de3036261ef42deebd931a6a291"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4393,7 +4397,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4427,7 +4431,7 @@ checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.18",
|
"quote 1.0.18",
|
||||||
"syn 1.0.96",
|
"syn 1.0.107",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Build base image
|
|
||||||
docker build -t revolt.chat/base:latest -f Dockerfile .
|
|
||||||
|
|
||||||
# Build crates
|
|
||||||
docker build -t revolt.chat/delta:latest -f crates/delta/Dockerfile .
|
|
||||||
docker build -t revolt.chat/bonfire:latest -f crates/bonfire/Dockerfile .
|
|
||||||
Vendored
-2
@@ -1,2 +0,0 @@
|
|||||||
ko_fi: insertish
|
|
||||||
custom: https://insrt.uk/donate
|
|
||||||
-98
@@ -1,98 +0,0 @@
|
|||||||
name: Docker
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- "master"
|
|
||||||
tags:
|
|
||||||
- "*"
|
|
||||||
paths-ignore:
|
|
||||||
- ".github/**"
|
|
||||||
- "!.github/workflows/docker.yml"
|
|
||||||
- ".vscode/**"
|
|
||||||
- ".gitignore"
|
|
||||||
- ".gitlab-ci.yml"
|
|
||||||
- "LICENSE"
|
|
||||||
- "README"
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- "master"
|
|
||||||
paths:
|
|
||||||
- "Dockerfile"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
architecture: [linux/amd64]
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: "recursive"
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Cache Docker layers
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache/${{ matrix.architecture }}
|
|
||||||
key: ${{ runner.os }}-buildx-${{ matrix.architecture }}-${{ github.sha }}
|
|
||||||
- name: Build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: ${{ matrix.architecture }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache/${{ matrix.architecture }}
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new/${{ matrix.architecture }},mode=max
|
|
||||||
- name: Move cache
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/.buildx-cache/${{ matrix.architecture }}
|
|
||||||
mv /tmp/.buildx-cache-new/${{ matrix.architecture }} /tmp/.buildx-cache/${{ matrix.architecture }}
|
|
||||||
|
|
||||||
publish_amd64:
|
|
||||||
needs: [test]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: "recursive"
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Cache amd64 Docker layers
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache/linux/amd64
|
|
||||||
key: ${{ runner.os }}-buildx-linux/amd64-${{ github.sha }}
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: ghcr.io/revoltchat/bonfire
|
|
||||||
- name: Login to Github Container Registry
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Build and publish
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: linux/amd64
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache/linux/amd64
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
|
||||||
- name: Move cache
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/.buildx-cache
|
|
||||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
||||||
-33
@@ -1,33 +0,0 @@
|
|||||||
name: Rust build and test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
name: Rust project
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Install latest nightly
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: nightly
|
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
|
||||||
|
|
||||||
- name: Run cargo build
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
|
|
||||||
- name: Run cargo test
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
name: Add Issue to Board
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
track_issue:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Get project data
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
run: |
|
|
||||||
gh api graphql -f query='
|
|
||||||
query {
|
|
||||||
organization(login: "revoltchat"){
|
|
||||||
projectNext(number: 3) {
|
|
||||||
id
|
|
||||||
fields(first:20) {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' > project_data.json
|
|
||||||
|
|
||||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Add issue to project
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
|
||||||
run: |
|
|
||||||
item_id="$( gh api graphql -f query='
|
|
||||||
mutation($project:ID!, $issue:ID!) {
|
|
||||||
addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
|
|
||||||
projectNextItem {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
|
||||||
|
|
||||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
|
||||||
-72
@@ -1,72 +0,0 @@
|
|||||||
name: Add PR to Board
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, synchronize, ready_for_review, review_requested]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
track_pr:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Get project data
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
run: |
|
|
||||||
gh api graphql -f query='
|
|
||||||
query {
|
|
||||||
organization(login: "revoltchat"){
|
|
||||||
projectNext(number: 3) {
|
|
||||||
id
|
|
||||||
fields(first:20) {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' > project_data.json
|
|
||||||
|
|
||||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Add PR to project
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
PR_ID: ${{ github.event.pull_request.node_id }}
|
|
||||||
run: |
|
|
||||||
item_id="$( gh api graphql -f query='
|
|
||||||
mutation($project:ID!, $pr:ID!) {
|
|
||||||
addProjectNextItem(input: {projectId: $project, contentId: $pr}) {
|
|
||||||
projectNextItem {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
|
||||||
|
|
||||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set fields
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
run: |
|
|
||||||
gh api graphql -f query='
|
|
||||||
mutation (
|
|
||||||
$project: ID!
|
|
||||||
$item: ID!
|
|
||||||
$status_field: ID!
|
|
||||||
$status_value: String!
|
|
||||||
) {
|
|
||||||
set_status: updateProjectNextItemField(input: {
|
|
||||||
projectId: $project
|
|
||||||
itemId: $item
|
|
||||||
fieldId: $status_field
|
|
||||||
value: $status_value
|
|
||||||
}) {
|
|
||||||
projectNextItem {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.6"
|
version = "0.5.16"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.6"
|
version = "0.5.16"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -52,7 +52,7 @@ mobc-redis = { version = "0.7.0", default-features = false, features = ["async-s
|
|||||||
# web
|
# web
|
||||||
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
||||||
rocket_empty = { version = "0.1.1", features = ["schema"] }
|
rocket_empty = { version = "0.1.1", features = ["schema"] }
|
||||||
rocket_rauth = { git = "https://github.com/insertish/rauth", tag = "1.0.3" }
|
rocket_authifier = { version = "1.0.7" }
|
||||||
|
|
||||||
# spec generation
|
# spec generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vergen(Config::default()).unwrap();
|
vergen(Config::default()).ok();
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -11,8 +11,8 @@ pub mod routes;
|
|||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
use async_std::channel::unbounded;
|
use async_std::channel::unbounded;
|
||||||
|
use revolt_quark::authifier::{Authifier, AuthifierEvent};
|
||||||
use revolt_quark::events::client::EventV1;
|
use revolt_quark::events::client::EventV1;
|
||||||
use revolt_quark::rauth::{RAuth, RAuthEvent};
|
|
||||||
use revolt_quark::DatabaseInfo;
|
use revolt_quark::DatabaseInfo;
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
@@ -27,25 +27,25 @@ async fn rocket() -> _ {
|
|||||||
let db = DatabaseInfo::Auto.connect().await.unwrap();
|
let db = DatabaseInfo::Auto.connect().await.unwrap();
|
||||||
db.migrate_database().await.unwrap();
|
db.migrate_database().await.unwrap();
|
||||||
|
|
||||||
// Setup rAuth event channel
|
// Setup Authifier event channel
|
||||||
let (sender, receiver) = unbounded();
|
let (sender, receiver) = unbounded();
|
||||||
|
|
||||||
// Setup rAuth
|
// Setup Authifier
|
||||||
let rauth = RAuth {
|
let authifier = Authifier {
|
||||||
database: db.clone().into(),
|
database: db.clone().into(),
|
||||||
config: revolt_quark::util::rauth::config(),
|
config: revolt_quark::util::authifier::config(),
|
||||||
event_channel: Some(sender),
|
event_channel: Some(sender),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Launch a listener for rAuth events
|
// Launch a listener for Authifier events
|
||||||
async_std::task::spawn(async move {
|
async_std::task::spawn(async move {
|
||||||
while let Ok(event) = receiver.recv().await {
|
while let Ok(event) = receiver.recv().await {
|
||||||
match &event {
|
match &event {
|
||||||
RAuthEvent::CreateSession { .. } | RAuthEvent::CreateAccount { .. } => {
|
AuthifierEvent::CreateSession { .. } | AuthifierEvent::CreateAccount { .. } => {
|
||||||
EventV1::Auth(event).global().await
|
EventV1::Auth(event).global().await
|
||||||
}
|
}
|
||||||
RAuthEvent::DeleteSession { user_id, .. }
|
AuthifierEvent::DeleteSession { user_id, .. }
|
||||||
| RAuthEvent::DeleteAllSessions { user_id, .. } => {
|
| AuthifierEvent::DeleteAllSessions { user_id, .. } => {
|
||||||
let id = user_id.to_string();
|
let id = user_id.to_string();
|
||||||
EventV1::Auth(event).private(id).await
|
EventV1::Auth(event).private(id).await
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ async fn rocket() -> _ {
|
|||||||
.mount("/", revolt_quark::web::cors::catch_all_options_routes())
|
.mount("/", revolt_quark::web::cors::catch_all_options_routes())
|
||||||
.mount("/", revolt_quark::web::ratelimiter::routes())
|
.mount("/", revolt_quark::web::ratelimiter::routes())
|
||||||
.mount("/swagger/", revolt_quark::web::swagger::routes())
|
.mount("/swagger/", revolt_quark::web::swagger::routes())
|
||||||
.manage(rauth)
|
.manage(authifier)
|
||||||
.manage(db)
|
.manage(db)
|
||||||
.manage(cors.clone())
|
.manage(cors.clone())
|
||||||
.attach(revolt_quark::web::ratelimiter::RatelimitFairing)
|
.attach(revolt_quark::web::ratelimiter::RatelimitFairing)
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use revolt_quark::{
|
||||||
|
models::{
|
||||||
|
message::{BulkMessageResponse, MessageQuery},
|
||||||
|
User,
|
||||||
|
},
|
||||||
|
Db, Error, Result,
|
||||||
|
};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Globally Fetch Messages
|
||||||
|
///
|
||||||
|
/// This is a privileged route to globally fetch messages.
|
||||||
|
#[openapi(tag = "Admin")]
|
||||||
|
#[post("/messages", data = "<data>")]
|
||||||
|
pub async fn message_query(
|
||||||
|
db: &Db,
|
||||||
|
user: User,
|
||||||
|
data: Json<MessageQuery>,
|
||||||
|
) -> Result<Json<BulkMessageResponse>> {
|
||||||
|
// Must be privileged for this route
|
||||||
|
if !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch data using query
|
||||||
|
let data = data.into_inner();
|
||||||
|
let messages = db.fetch_messages(data).await?;
|
||||||
|
BulkMessageResponse::transform(db, None, messages, Some(true))
|
||||||
|
.await
|
||||||
|
.map(Json)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod message_query;
|
||||||
|
mod stats;
|
||||||
|
|
||||||
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
|
openapi_get_routes_spec![stats::stats, message_query::message_query]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
use revolt_quark::models::stats::Stats;
|
||||||
|
use revolt_quark::{Db, Result};
|
||||||
|
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Query Stats
|
||||||
|
///
|
||||||
|
/// Fetch various technical statistics.
|
||||||
|
#[openapi(tag = "Admin")]
|
||||||
|
#[get("/stats")]
|
||||||
|
pub async fn stats(db: &Db) -> Result<Json<Stats>> {
|
||||||
|
Ok(Json(db.generate_stats().await?))
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::message::{PartialMessage, SendableEmbed},
|
models::message::{PartialMessage, SendableEmbed},
|
||||||
models::{Message, User},
|
models::{Message, User},
|
||||||
|
perms,
|
||||||
types::january::Embed,
|
types::january::Embed,
|
||||||
Db, Error, Ref, Result, Timestamp,
|
Db, Error, Permission, Ref, Result, Timestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -28,7 +29,7 @@ pub struct DataEditMessage {
|
|||||||
pub async fn req(
|
pub async fn req(
|
||||||
db: &Db,
|
db: &Db,
|
||||||
user: User,
|
user: User,
|
||||||
target: String,
|
target: Ref,
|
||||||
msg: Ref,
|
msg: Ref,
|
||||||
edit: Json<DataEditMessage>,
|
edit: Json<DataEditMessage>,
|
||||||
) -> Result<Json<Message>> {
|
) -> Result<Json<Message>> {
|
||||||
@@ -36,10 +37,17 @@ pub async fn req(
|
|||||||
edit.validate()
|
edit.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
// Ensure we have permissions to send a message
|
||||||
|
let channel = target.as_channel(db).await?;
|
||||||
|
let mut permissions = perms(&user).channel(&channel);
|
||||||
|
permissions
|
||||||
|
.throw_permission_and_view_channel(db, Permission::SendMessage)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Message::validate_sum(&edit.content, &edit.embeds)?;
|
Message::validate_sum(&edit.content, &edit.embeds)?;
|
||||||
|
|
||||||
let mut message = msg.as_message(db).await?;
|
let mut message = msg.as_message(db).await?;
|
||||||
if message.channel != target {
|
if message.channel != channel.id() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::{
|
models::{
|
||||||
message::{BulkMessageResponse, MessageSort},
|
message::{
|
||||||
|
BulkMessageResponse, MessageFilter, MessageQuery, MessageSort, MessageTimePeriod,
|
||||||
|
},
|
||||||
User,
|
User,
|
||||||
},
|
},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms, Db, Error, Permission, Ref, Result,
|
||||||
@@ -68,14 +70,28 @@ pub async fn req(
|
|||||||
sort,
|
sort,
|
||||||
nearby,
|
nearby,
|
||||||
include_users,
|
include_users,
|
||||||
..
|
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let messages = db
|
let messages = db
|
||||||
.fetch_messages(channel.id(), limit, before, after, sort, nearby)
|
.fetch_messages(MessageQuery {
|
||||||
|
filter: MessageFilter {
|
||||||
|
channel: Some(channel.id().to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
time_period: if let Some(nearby) = nearby {
|
||||||
|
MessageTimePeriod::Relative { nearby }
|
||||||
|
} else {
|
||||||
|
MessageTimePeriod::Absolute {
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
sort,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
limit,
|
||||||
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
BulkMessageResponse::transform(db, &channel, messages, include_users)
|
BulkMessageResponse::transform(db, Some(&channel), messages, include_users)
|
||||||
.await
|
.await
|
||||||
.map(Json)
|
.map(Json)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::{
|
models::{
|
||||||
message::{BulkMessageResponse, MessageSort},
|
message::{
|
||||||
|
BulkMessageResponse, MessageFilter, MessageQuery, MessageSort, MessageTimePeriod,
|
||||||
|
},
|
||||||
User,
|
User,
|
||||||
},
|
},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms, Db, Error, Permission, Ref, Result,
|
||||||
@@ -30,7 +32,7 @@ pub struct OptionsMessageSearch {
|
|||||||
after: Option<String>,
|
after: Option<String>,
|
||||||
/// Message sort direction
|
/// Message sort direction
|
||||||
///
|
///
|
||||||
/// By default, it will be sorted by relevance.
|
/// By default, it will be sorted by latest.
|
||||||
#[serde(default = "MessageSort::default")]
|
#[serde(default = "MessageSort::default")]
|
||||||
sort: MessageSort,
|
sort: MessageSort,
|
||||||
/// Whether to include user (and member, if server channel) objects
|
/// Whether to include user (and member, if server channel) objects
|
||||||
@@ -73,10 +75,22 @@ pub async fn req(
|
|||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let messages = db
|
let messages = db
|
||||||
.search_messages(channel.id(), &query, limit, before, after, sort)
|
.fetch_messages(MessageQuery {
|
||||||
|
filter: MessageFilter {
|
||||||
|
channel: Some(channel.id().to_string()),
|
||||||
|
query: Some(query),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
time_period: MessageTimePeriod::Absolute {
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
sort: Some(sort),
|
||||||
|
},
|
||||||
|
limit,
|
||||||
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
BulkMessageResponse::transform(db, &channel, messages, include_users)
|
BulkMessageResponse::transform(db, Some(&channel), messages, include_users)
|
||||||
.await
|
.await
|
||||||
.map(Json)
|
.map(Json)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,16 +65,20 @@ pub async fn message_send(
|
|||||||
data.validate()
|
data.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
// Validate Message is within reasonable length limits
|
||||||
Message::validate_sum(&data.content, &data.embeds)?;
|
Message::validate_sum(&data.content, &data.embeds)?;
|
||||||
|
|
||||||
|
// Ensure the request is unique
|
||||||
idempotency.consume_nonce(data.nonce).await?;
|
idempotency.consume_nonce(data.nonce).await?;
|
||||||
|
|
||||||
|
// Ensure we have permissions to send a message
|
||||||
let channel = target.as_channel(db).await?;
|
let channel = target.as_channel(db).await?;
|
||||||
let mut permissions = perms(&user).channel(&channel);
|
let mut permissions = perms(&user).channel(&channel);
|
||||||
permissions
|
permissions
|
||||||
.throw_permission_and_view_channel(db, Permission::SendMessage)
|
.throw_permission_and_view_channel(db, Permission::SendMessage)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Check the message is not empty
|
||||||
if (data.content.as_ref().map_or(true, |v| v.is_empty()))
|
if (data.content.as_ref().map_or(true, |v| v.is_empty()))
|
||||||
&& (data.attachments.as_ref().map_or(true, |v| v.is_empty()))
|
&& (data.attachments.as_ref().map_or(true, |v| v.is_empty()))
|
||||||
&& (data.embeds.as_ref().map_or(true, |v| v.is_empty()))
|
&& (data.embeds.as_ref().map_or(true, |v| v.is_empty()))
|
||||||
@@ -82,6 +86,22 @@ pub async fn message_send(
|
|||||||
return Err(Error::EmptyMessage);
|
return Err(Error::EmptyMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure restrict_reactions is not specified without reactions list
|
||||||
|
if let Some(interactions) = &data.interactions {
|
||||||
|
if interactions.restrict_reactions {
|
||||||
|
let disallowed = if let Some(list) = &interactions.reactions {
|
||||||
|
list.is_empty()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if disallowed {
|
||||||
|
return Err(Error::InvalidProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start constructing the message
|
||||||
let message_id = Ulid::new().to_string();
|
let message_id = Ulid::new().to_string();
|
||||||
let mut message = Message {
|
let mut message = Message {
|
||||||
id: message_id.clone(),
|
id: message_id.clone(),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pub use rocket::http::Status;
|
|||||||
pub use rocket::response::Redirect;
|
pub use rocket::response::Redirect;
|
||||||
use rocket::{Build, Rocket};
|
use rocket::{Build, Rocket};
|
||||||
|
|
||||||
|
mod admin;
|
||||||
mod bots;
|
mod bots;
|
||||||
mod channels;
|
mod channels;
|
||||||
mod customisation;
|
mod customisation;
|
||||||
@@ -10,6 +11,7 @@ mod invites;
|
|||||||
mod onboard;
|
mod onboard;
|
||||||
mod push;
|
mod push;
|
||||||
mod root;
|
mod root;
|
||||||
|
mod safety;
|
||||||
mod servers;
|
mod servers;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod users;
|
mod users;
|
||||||
@@ -21,15 +23,17 @@ pub fn mount(mut rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
rocket, "/".to_owned(), settings,
|
rocket, "/".to_owned(), settings,
|
||||||
"/" => (vec![], custom_openapi_spec()),
|
"/" => (vec![], custom_openapi_spec()),
|
||||||
"" => openapi_get_routes_spec![root::root, root::ping],
|
"" => openapi_get_routes_spec![root::root, root::ping],
|
||||||
|
"/admin" => admin::routes(),
|
||||||
"/users" => users::routes(),
|
"/users" => users::routes(),
|
||||||
"/bots" => bots::routes(),
|
"/bots" => bots::routes(),
|
||||||
"/channels" => channels::routes(),
|
"/channels" => channels::routes(),
|
||||||
"/servers" => servers::routes(),
|
"/servers" => servers::routes(),
|
||||||
"/invites" => invites::routes(),
|
"/invites" => invites::routes(),
|
||||||
"/custom" => customisation::routes(),
|
"/custom" => customisation::routes(),
|
||||||
"/auth/account" => rocket_rauth::routes::account::routes(),
|
"/safety" => safety::routes(),
|
||||||
"/auth/session" => rocket_rauth::routes::session::routes(),
|
"/auth/account" => rocket_authifier::routes::account::routes(),
|
||||||
"/auth/mfa" => rocket_rauth::routes::mfa::routes(),
|
"/auth/session" => rocket_authifier::routes::session::routes(),
|
||||||
|
"/auth/mfa" => rocket_authifier::routes::mfa::routes(),
|
||||||
"/onboard" => onboard::routes(),
|
"/onboard" => onboard::routes(),
|
||||||
"/push" => push::routes(),
|
"/push" => push::routes(),
|
||||||
"/sync" => sync::routes(),
|
"/sync" => sync::routes(),
|
||||||
@@ -105,6 +109,13 @@ fn custom_openapi_spec() -> OpenApi {
|
|||||||
"Emojis"
|
"Emojis"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Platform Administration",
|
||||||
|
"tags": [
|
||||||
|
"Admin",
|
||||||
|
"User Safety"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Authentication",
|
"name": "Authentication",
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::util::regex::RE_USERNAME;
|
use crate::util::regex::RE_USERNAME;
|
||||||
use revolt_quark::{models::User, rauth::models::Session, Database, EmptyResponse, Error, Result};
|
use revolt_quark::{
|
||||||
|
authifier::models::Session, models::User, Database, EmptyResponse, Error, Result,
|
||||||
|
};
|
||||||
|
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use revolt_quark::{models::User, rauth::models::Session};
|
use revolt_quark::{authifier::models::Session, models::User};
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
rauth::{
|
authifier::{
|
||||||
models::{Session, WebPushSubscription},
|
models::{Session, WebPushSubscription},
|
||||||
RAuth,
|
Authifier,
|
||||||
},
|
},
|
||||||
EmptyResponse, Error, Result,
|
EmptyResponse, Error, Result,
|
||||||
};
|
};
|
||||||
@@ -16,13 +16,13 @@ use rocket::{serde::json::Json, State};
|
|||||||
#[openapi(tag = "Web Push")]
|
#[openapi(tag = "Web Push")]
|
||||||
#[post("/subscribe", data = "<data>")]
|
#[post("/subscribe", data = "<data>")]
|
||||||
pub async fn req(
|
pub async fn req(
|
||||||
rauth: &State<RAuth>,
|
authifier: &State<Authifier>,
|
||||||
mut session: Session,
|
mut session: Session,
|
||||||
data: Json<WebPushSubscription>,
|
data: Json<WebPushSubscription>,
|
||||||
) -> Result<EmptyResponse> {
|
) -> Result<EmptyResponse> {
|
||||||
session.subscription = Some(data.into_inner());
|
session.subscription = Some(data.into_inner());
|
||||||
session
|
session
|
||||||
.save(rauth)
|
.save(authifier)
|
||||||
.await
|
.await
|
||||||
.map(|_| EmptyResponse)
|
.map(|_| EmptyResponse)
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
rauth::{models::Session, RAuth},
|
authifier::{models::Session, Authifier},
|
||||||
EmptyResponse, Error, Result,
|
EmptyResponse, Error, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -10,10 +10,10 @@ use rocket::State;
|
|||||||
/// Remove the Web Push subscription associated with the current session.
|
/// Remove the Web Push subscription associated with the current session.
|
||||||
#[openapi(tag = "Web Push")]
|
#[openapi(tag = "Web Push")]
|
||||||
#[post("/unsubscribe")]
|
#[post("/unsubscribe")]
|
||||||
pub async fn req(rauth: &State<RAuth>, mut session: Session) -> Result<EmptyResponse> {
|
pub async fn req(authifier: &State<Authifier>, mut session: Session) -> Result<EmptyResponse> {
|
||||||
session.subscription = None;
|
session.subscription = None;
|
||||||
session
|
session
|
||||||
.save(rauth)
|
.save(authifier)
|
||||||
.await
|
.await
|
||||||
.map(|_| EmptyResponse)
|
.map(|_| EmptyResponse)
|
||||||
.map_err(|_| Error::DatabaseError {
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
|||||||
@@ -119,11 +119,21 @@ pub async fn root() -> Result<Json<RevoltConfig>> {
|
|||||||
app: APP_URL.to_string(),
|
app: APP_URL.to_string(),
|
||||||
vapid: VAPID_PUBLIC_KEY.to_string(),
|
vapid: VAPID_PUBLIC_KEY.to_string(),
|
||||||
build: BuildInformation {
|
build: BuildInformation {
|
||||||
commit_sha: env!("VERGEN_GIT_SHA").to_string(),
|
commit_sha: option_env!("VERGEN_GIT_SHA")
|
||||||
commit_timestamp: env!("VERGEN_GIT_COMMIT_TIMESTAMP").to_string(),
|
.unwrap_or_else(|| "<failed to generate>")
|
||||||
semver: env!("VERGEN_GIT_SEMVER").to_string(),
|
.to_string(),
|
||||||
origin_url: env!("GIT_ORIGIN_URL", "<missing>").to_string(),
|
commit_timestamp: option_env!("VERGEN_GIT_COMMIT_TIMESTAMP")
|
||||||
timestamp: env!("VERGEN_BUILD_TIMESTAMP").to_string(),
|
.unwrap_or_else(|| "<failed to generate>")
|
||||||
|
.to_string(),
|
||||||
|
semver: option_env!("VERGEN_GIT_SEMVER")
|
||||||
|
.unwrap_or_else(|| "<failed to generate>")
|
||||||
|
.to_string(),
|
||||||
|
origin_url: option_env!("GIT_ORIGIN_URL")
|
||||||
|
.unwrap_or_else(|| "<failed to generate>")
|
||||||
|
.to_string(),
|
||||||
|
timestamp: option_env!("VERGEN_BUILD_TIMESTAMP")
|
||||||
|
.unwrap_or_else(|| "<failed to generate>")
|
||||||
|
.to_string(),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use revolt_quark::{
|
||||||
|
models::{
|
||||||
|
report::{PartialReport, ReportStatus},
|
||||||
|
Report, User,
|
||||||
|
},
|
||||||
|
Db, Error, Ref, Result,
|
||||||
|
};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
/// # Report Data
|
||||||
|
#[derive(Validate, Deserialize, JsonSchema)]
|
||||||
|
pub struct DataEditReport {
|
||||||
|
/// New report status
|
||||||
|
status: Option<ReportStatus>,
|
||||||
|
/// Report notes
|
||||||
|
notes: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Edit Report
|
||||||
|
///
|
||||||
|
/// Edit a report.
|
||||||
|
#[openapi(tag = "User Safety")]
|
||||||
|
#[patch("/reports/<report>", data = "<edit>")]
|
||||||
|
pub async fn edit_report(
|
||||||
|
db: &Db,
|
||||||
|
user: User,
|
||||||
|
report: Ref,
|
||||||
|
edit: Json<DataEditReport>,
|
||||||
|
) -> Result<Json<Report>> {
|
||||||
|
// Must be privileged for this route
|
||||||
|
if !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate data
|
||||||
|
let edit = edit.into_inner();
|
||||||
|
edit.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
// Create and apply update to report
|
||||||
|
let mut report = report.as_report(db).await?;
|
||||||
|
report
|
||||||
|
.update(
|
||||||
|
db,
|
||||||
|
PartialReport {
|
||||||
|
status: edit.status,
|
||||||
|
notes: edit.notes,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(report))
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use revolt_quark::models::{Report, User};
|
||||||
|
use revolt_quark::{Db, Error, Result};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Fetch Report
|
||||||
|
///
|
||||||
|
/// Fetch a report by its ID
|
||||||
|
#[openapi(tag = "User Safety")]
|
||||||
|
#[get("/report/<id>")]
|
||||||
|
pub async fn fetch_report(db: &Db, user: User, id: String) -> Result<Json<Report>> {
|
||||||
|
// Must be privileged for this route
|
||||||
|
if !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.fetch_report(&id).await.map(Json)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use revolt_quark::models::{Report, User};
|
||||||
|
use revolt_quark::{Db, Error, Result};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Fetch Reports
|
||||||
|
///
|
||||||
|
/// Fetch all available reports
|
||||||
|
#[openapi(tag = "User Safety")]
|
||||||
|
#[get("/reports")]
|
||||||
|
pub async fn fetch_reports(db: &Db, user: User) -> Result<Json<Vec<Report>>> {
|
||||||
|
// Must be privileged for this route
|
||||||
|
if !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.fetch_reports().await.map(Json)
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use revolt_quark::models::snapshot::{SnapshotContent, SnapshotWithContext};
|
||||||
|
use revolt_quark::models::{Channel, User};
|
||||||
|
use revolt_quark::{Db, Error, Result};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Fetch Snapshot
|
||||||
|
///
|
||||||
|
/// Fetch a snapshot for a given report
|
||||||
|
#[openapi(tag = "User Safety")]
|
||||||
|
#[get("/snapshot/<report_id>")]
|
||||||
|
pub async fn fetch_snapshot(
|
||||||
|
db: &Db,
|
||||||
|
user: User,
|
||||||
|
report_id: String,
|
||||||
|
) -> Result<Json<SnapshotWithContext>> {
|
||||||
|
// Must be privileged for this route
|
||||||
|
if !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch snapshot
|
||||||
|
let snapshot = db.fetch_snapshot(&report_id).await?;
|
||||||
|
|
||||||
|
// Resolve and fetch IDs of associated content
|
||||||
|
let mut user_ids: HashSet<&str> = HashSet::new();
|
||||||
|
let mut channel_ids: HashSet<&str> = HashSet::new();
|
||||||
|
|
||||||
|
match &snapshot.content {
|
||||||
|
SnapshotContent::Message {
|
||||||
|
prior_context,
|
||||||
|
leading_context,
|
||||||
|
message,
|
||||||
|
} => {
|
||||||
|
for msg in prior_context {
|
||||||
|
user_ids.insert(&msg.author);
|
||||||
|
}
|
||||||
|
|
||||||
|
for msg in leading_context {
|
||||||
|
user_ids.insert(&msg.author);
|
||||||
|
}
|
||||||
|
|
||||||
|
user_ids.insert(&message.author);
|
||||||
|
channel_ids.insert(&message.channel);
|
||||||
|
}
|
||||||
|
SnapshotContent::User(user) => {
|
||||||
|
user_ids.insert(&user.id);
|
||||||
|
}
|
||||||
|
SnapshotContent::Server(server) => {
|
||||||
|
for channel in &server.channels {
|
||||||
|
channel_ids.insert(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect user and channel IDs
|
||||||
|
let user_ids: Vec<String> = user_ids.into_iter().map(|s| s.to_owned()).collect();
|
||||||
|
let channel_ids: Vec<String> = channel_ids.into_iter().map(|s| s.to_owned()).collect();
|
||||||
|
|
||||||
|
// Fetch users and channels
|
||||||
|
let users = db.fetch_users(&user_ids).await?;
|
||||||
|
let channels = db.fetch_channels(&channel_ids).await?;
|
||||||
|
|
||||||
|
// Pull out first server from channels if possible
|
||||||
|
let server = if let Some(server_id) = channels.iter().find_map(|channel| match channel {
|
||||||
|
Channel::TextChannel { server, .. } => Some(server),
|
||||||
|
_ => None,
|
||||||
|
}) {
|
||||||
|
Some(db.fetch_server(server_id).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return snapshot with context
|
||||||
|
Ok(Json(SnapshotWithContext {
|
||||||
|
snapshot,
|
||||||
|
users,
|
||||||
|
channels,
|
||||||
|
server,
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
||||||
|
use rocket::Route;
|
||||||
|
|
||||||
|
mod edit_report;
|
||||||
|
mod fetch_report;
|
||||||
|
mod fetch_reports;
|
||||||
|
mod report_content;
|
||||||
|
|
||||||
|
mod fetch_snapshot;
|
||||||
|
|
||||||
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
|
openapi_get_routes_spec![
|
||||||
|
// Reports
|
||||||
|
edit_report::edit_report,
|
||||||
|
fetch_report::fetch_report,
|
||||||
|
fetch_reports::fetch_reports,
|
||||||
|
report_content::report_content,
|
||||||
|
// Snapshots
|
||||||
|
fetch_snapshot::fetch_snapshot
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
use revolt_quark::events::client::EventV1;
|
||||||
|
use revolt_quark::models::message::{MessageFilter, MessageQuery, MessageSort, MessageTimePeriod};
|
||||||
|
use revolt_quark::models::report::{ReportStatus, ReportedContent};
|
||||||
|
use revolt_quark::models::snapshot::{Snapshot, SnapshotContent};
|
||||||
|
use revolt_quark::models::{Report, User};
|
||||||
|
use revolt_quark::{Db, Error, Result};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use ulid::Ulid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Report Data
|
||||||
|
#[derive(Validate, Deserialize, JsonSchema)]
|
||||||
|
pub struct DataReportContent {
|
||||||
|
/// Content being reported
|
||||||
|
content: ReportedContent,
|
||||||
|
/// Additional report description
|
||||||
|
#[validate(length(min = 0, max = 1000))]
|
||||||
|
#[serde(default)]
|
||||||
|
additional_context: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Report Content
|
||||||
|
///
|
||||||
|
/// Report a piece of content to the moderation team.
|
||||||
|
#[openapi(tag = "User Safety")]
|
||||||
|
#[post("/report", data = "<data>")]
|
||||||
|
pub async fn report_content(db: &Db, user: User, data: Json<DataReportContent>) -> Result<()> {
|
||||||
|
let data = data.into_inner();
|
||||||
|
data.validate()
|
||||||
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
// Bots cannot create reports
|
||||||
|
if user.bot.is_some() {
|
||||||
|
return Err(Error::IsBot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the content and create a snapshot of it
|
||||||
|
// Also retrieve any references to Files
|
||||||
|
let (content, files): (SnapshotContent, Vec<String>) = match &data.content {
|
||||||
|
ReportedContent::Message { id, .. } => {
|
||||||
|
let message = db.fetch_message(id).await?;
|
||||||
|
|
||||||
|
// Users cannot report themselves
|
||||||
|
if message.author == user.id {
|
||||||
|
return Err(Error::CannotReportYourself);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect message attachments
|
||||||
|
let files = message
|
||||||
|
.attachments
|
||||||
|
.as_ref()
|
||||||
|
.map(|attachments| attachments.iter().map(|x| x.id.to_string()).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// Collect prior context
|
||||||
|
let prior_context = db
|
||||||
|
.fetch_messages(MessageQuery {
|
||||||
|
filter: MessageFilter {
|
||||||
|
channel: Some(message.channel.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
limit: Some(15),
|
||||||
|
time_period: MessageTimePeriod::Absolute {
|
||||||
|
before: Some(message.id.to_string()),
|
||||||
|
after: None,
|
||||||
|
sort: Some(MessageSort::Latest),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Collect leading context
|
||||||
|
let leading_context = db
|
||||||
|
.fetch_messages(MessageQuery {
|
||||||
|
filter: MessageFilter {
|
||||||
|
channel: Some(message.channel.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
limit: Some(15),
|
||||||
|
time_period: MessageTimePeriod::Absolute {
|
||||||
|
before: None,
|
||||||
|
after: Some(message.id.to_string()),
|
||||||
|
sort: Some(MessageSort::Oldest),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
(
|
||||||
|
SnapshotContent::Message {
|
||||||
|
message,
|
||||||
|
prior_context,
|
||||||
|
leading_context,
|
||||||
|
},
|
||||||
|
files,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ReportedContent::Server { id, .. } => {
|
||||||
|
let server = db.fetch_server(id).await?;
|
||||||
|
|
||||||
|
// Users cannot report their own server
|
||||||
|
if server.owner == user.id {
|
||||||
|
return Err(Error::CannotReportYourself);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect server's icon and banner
|
||||||
|
let files = [&server.icon, &server.banner]
|
||||||
|
.iter()
|
||||||
|
.filter_map(|x| x.as_ref().map(|x| x.id.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
(SnapshotContent::Server(server), files)
|
||||||
|
}
|
||||||
|
ReportedContent::User { id, .. } => {
|
||||||
|
let reported_user = db.fetch_user(id).await?;
|
||||||
|
|
||||||
|
// Users cannot report themselves
|
||||||
|
if reported_user.id == user.id {
|
||||||
|
return Err(Error::CannotReportYourself);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect user's avatar and profile background
|
||||||
|
let files = [
|
||||||
|
reported_user.avatar.as_ref(),
|
||||||
|
reported_user
|
||||||
|
.profile
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|profile| profile.background.as_ref()),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.filter_map(|x| x.as_ref().map(|x| x.id.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
(SnapshotContent::User(reported_user), files)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mark all the attachments as reported
|
||||||
|
for file in files {
|
||||||
|
db.mark_attachment_as_reported(&file).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate an id for the report
|
||||||
|
let id = Ulid::new().to_string();
|
||||||
|
|
||||||
|
// Save a snapshot of the content
|
||||||
|
let snapshot = Snapshot {
|
||||||
|
id: Ulid::new().to_string(),
|
||||||
|
report_id: id.to_string(),
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_snapshot(&snapshot).await?;
|
||||||
|
|
||||||
|
// Save the report
|
||||||
|
let report = Report {
|
||||||
|
id,
|
||||||
|
author_id: user.id,
|
||||||
|
content: data.content,
|
||||||
|
additional_context: data.additional_context,
|
||||||
|
status: ReportStatus::Created {},
|
||||||
|
notes: String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_report(&report).await?;
|
||||||
|
|
||||||
|
EventV1::ReportCreate(report).global().await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -33,6 +33,10 @@ pub struct DataEditServer {
|
|||||||
/// System message configuration
|
/// System message configuration
|
||||||
system_messages: Option<SystemMessageChannels>,
|
system_messages: Option<SystemMessageChannels>,
|
||||||
|
|
||||||
|
/// Bitfield of server flags
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub flags: Option<i32>,
|
||||||
|
|
||||||
// Whether this server is age-restricted
|
// Whether this server is age-restricted
|
||||||
// nsfw: Option<bool>,
|
// nsfw: Option<bool>,
|
||||||
/// Whether this server is public and should show up on [Revolt Discover](https://rvlt.gg)
|
/// Whether this server is public and should show up on [Revolt Discover](https://rvlt.gg)
|
||||||
@@ -92,6 +96,11 @@ pub async fn req(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check we are privileged if changing sensitive fields
|
||||||
|
if data.flags.is_some() && !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
if data.categories.is_some() {
|
if data.categories.is_some() {
|
||||||
permissions
|
permissions
|
||||||
.throw_permission(db, Permission::ManageChannel)
|
.throw_permission(db, Permission::ManageChannel)
|
||||||
@@ -105,6 +114,7 @@ pub async fn req(
|
|||||||
banner,
|
banner,
|
||||||
categories,
|
categories,
|
||||||
system_messages,
|
system_messages,
|
||||||
|
flags,
|
||||||
// nsfw,
|
// nsfw,
|
||||||
discoverable,
|
discoverable,
|
||||||
analytics,
|
analytics,
|
||||||
@@ -116,6 +126,7 @@ pub async fn req(
|
|||||||
description,
|
description,
|
||||||
categories,
|
categories,
|
||||||
system_messages,
|
system_messages,
|
||||||
|
flags,
|
||||||
// nsfw,
|
// nsfw,
|
||||||
discoverable,
|
discoverable,
|
||||||
analytics,
|
analytics,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::util::regex::RE_USERNAME;
|
use crate::util::regex::RE_USERNAME;
|
||||||
use revolt_quark::{models::User, rauth::models::Account, Database, Error, Result};
|
use revolt_quark::{authifier::models::Account, models::User, Database, Error, Result};
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_quark::models::user::{FieldsUser, PartialUser, User};
|
use revolt_quark::models::user::{FieldsUser, PartialUser, User};
|
||||||
use revolt_quark::models::File;
|
use revolt_quark::models::File;
|
||||||
use revolt_quark::{Database, Error, Result};
|
use revolt_quark::{Database, Error, Ref, Result};
|
||||||
|
|
||||||
use revolt_quark::models::user::UserStatus;
|
use revolt_quark::models::user::UserStatus;
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -24,6 +24,10 @@ pub struct UserProfileData {
|
|||||||
/// # User Data
|
/// # User Data
|
||||||
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
|
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct DataEditUser {
|
pub struct DataEditUser {
|
||||||
|
/// Attachment Id for avatar
|
||||||
|
#[validate(length(min = 1, max = 128))]
|
||||||
|
avatar: Option<String>,
|
||||||
|
|
||||||
/// New user status
|
/// New user status
|
||||||
#[validate]
|
#[validate]
|
||||||
status: Option<UserStatus>,
|
status: Option<UserStatus>,
|
||||||
@@ -32,9 +36,14 @@ pub struct DataEditUser {
|
|||||||
/// This is applied as a partial.
|
/// This is applied as a partial.
|
||||||
#[validate]
|
#[validate]
|
||||||
profile: Option<UserProfileData>,
|
profile: Option<UserProfileData>,
|
||||||
/// Attachment Id for avatar
|
|
||||||
#[validate(length(min = 1, max = 128))]
|
/// Bitfield of user badges
|
||||||
avatar: Option<String>,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
badges: Option<i32>,
|
||||||
|
/// Enum of user flags
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
flags: Option<i32>,
|
||||||
|
|
||||||
/// Fields to remove from user object
|
/// Fields to remove from user object
|
||||||
#[validate(length(min = 1))]
|
#[validate(length(min = 1))]
|
||||||
remove: Option<Vec<FieldsUser>>,
|
remove: Option<Vec<FieldsUser>>,
|
||||||
@@ -44,19 +53,36 @@ pub struct DataEditUser {
|
|||||||
///
|
///
|
||||||
/// Edit currently authenticated user.
|
/// Edit currently authenticated user.
|
||||||
#[openapi(tag = "User Information")]
|
#[openapi(tag = "User Information")]
|
||||||
#[patch("/@me", data = "<data>")]
|
#[patch("/<target>", data = "<data>")]
|
||||||
pub async fn req(
|
pub async fn req(
|
||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
mut user: User,
|
mut user: User,
|
||||||
|
target: Ref,
|
||||||
data: Json<DataEditUser>,
|
data: Json<DataEditUser>,
|
||||||
) -> Result<Json<User>> {
|
) -> Result<Json<User>> {
|
||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
data.validate()
|
data.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
// If we want to edit a different user than self, ensure we have
|
||||||
|
// permissions and subsequently replace the user in question
|
||||||
|
if target.id != "@me" {
|
||||||
|
if !user.privileged {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
user = target.as_user(db).await?;
|
||||||
|
// Otherwise, filter out invalid edit fields
|
||||||
|
} else if data.badges.is_some() || data.flags.is_some() {
|
||||||
|
return Err(Error::NotPrivileged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit out early if nothing is changed
|
||||||
if data.status.is_none()
|
if data.status.is_none()
|
||||||
&& data.profile.is_none()
|
&& data.profile.is_none()
|
||||||
&& data.avatar.is_none()
|
&& data.avatar.is_none()
|
||||||
|
&& data.badges.is_none()
|
||||||
|
&& data.flags.is_none()
|
||||||
&& data.remove.is_none()
|
&& data.remove.is_none()
|
||||||
{
|
{
|
||||||
return Ok(Json(user));
|
return Ok(Json(user));
|
||||||
@@ -83,7 +109,11 @@ pub async fn req(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut partial: PartialUser = Default::default();
|
let mut partial: PartialUser = PartialUser {
|
||||||
|
badges: data.badges,
|
||||||
|
flags: data.flags,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
// 2. Apply new avatar
|
// 2. Apply new avatar
|
||||||
if let Some(avatar) = data.avatar {
|
if let Some(avatar) = data.avatar {
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
use revolt_quark::{Database, Ref, Result};
|
||||||
|
|
||||||
|
use rocket::{serde::json::Json, State};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// # Flag Response
|
||||||
|
#[derive(Serialize, JsonSchema)]
|
||||||
|
pub struct FlagResponse {
|
||||||
|
/// Flags
|
||||||
|
flags: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Fetch User Flags
|
||||||
|
///
|
||||||
|
/// Retrieve a user's flags.
|
||||||
|
#[openapi(tag = "User Information")]
|
||||||
|
#[get("/<target>/flags")]
|
||||||
|
pub async fn fetch_user_flags(db: &State<Database>, target: Ref) -> Result<Json<FlagResponse>> {
|
||||||
|
let flags = if let Ok(target) = target.as_user(db).await {
|
||||||
|
target.flags.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Json(FlagResponse { flags }))
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ mod fetch_dms;
|
|||||||
mod fetch_profile;
|
mod fetch_profile;
|
||||||
mod fetch_self;
|
mod fetch_self;
|
||||||
mod fetch_user;
|
mod fetch_user;
|
||||||
|
mod fetch_user_flags;
|
||||||
mod find_mutual;
|
mod find_mutual;
|
||||||
mod get_default_avatar;
|
mod get_default_avatar;
|
||||||
mod open_dm;
|
mod open_dm;
|
||||||
@@ -21,6 +22,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
// User Information
|
// User Information
|
||||||
fetch_self::req,
|
fetch_self::req,
|
||||||
fetch_user::req,
|
fetch_user::req,
|
||||||
|
fetch_user_flags::fetch_user_flags,
|
||||||
edit_user::req,
|
edit_user::req,
|
||||||
change_username::req,
|
change_username::req,
|
||||||
get_default_avatar::req,
|
get_default_avatar::req,
|
||||||
|
|||||||
Vendored
-2
@@ -1,2 +0,0 @@
|
|||||||
ko_fi: insertish
|
|
||||||
custom: https://insrt.uk/donate
|
|
||||||
-33
@@ -1,33 +0,0 @@
|
|||||||
name: Rust build and test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
name: Rust project
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Install latest nightly
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: nightly
|
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
|
||||||
|
|
||||||
- name: Run cargo build
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
|
|
||||||
- name: Run cargo test
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
name: Add Issue to Board
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
track_issue:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Get project data
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
run: |
|
|
||||||
gh api graphql -f query='
|
|
||||||
query {
|
|
||||||
organization(login: "revoltchat"){
|
|
||||||
projectNext(number: 3) {
|
|
||||||
id
|
|
||||||
fields(first:20) {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' > project_data.json
|
|
||||||
|
|
||||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Add issue to project
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
|
||||||
run: |
|
|
||||||
item_id="$( gh api graphql -f query='
|
|
||||||
mutation($project:ID!, $issue:ID!) {
|
|
||||||
addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
|
|
||||||
projectNextItem {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
|
||||||
|
|
||||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
|
||||||
-72
@@ -1,72 +0,0 @@
|
|||||||
name: Add PR to Board
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, synchronize, ready_for_review, review_requested]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
track_pr:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Get project data
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
run: |
|
|
||||||
gh api graphql -f query='
|
|
||||||
query {
|
|
||||||
organization(login: "revoltchat"){
|
|
||||||
projectNext(number: 3) {
|
|
||||||
id
|
|
||||||
fields(first:20) {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' > project_data.json
|
|
||||||
|
|
||||||
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
|
||||||
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Add PR to project
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
PR_ID: ${{ github.event.pull_request.node_id }}
|
|
||||||
run: |
|
|
||||||
item_id="$( gh api graphql -f query='
|
|
||||||
mutation($project:ID!, $pr:ID!) {
|
|
||||||
addProjectNextItem(input: {projectId: $project, contentId: $pr}) {
|
|
||||||
projectNextItem {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
|
|
||||||
|
|
||||||
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set fields
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
|
||||||
run: |
|
|
||||||
gh api graphql -f query='
|
|
||||||
mutation (
|
|
||||||
$project: ID!
|
|
||||||
$item: ID!
|
|
||||||
$status_field: ID!
|
|
||||||
$status_value: String!
|
|
||||||
) {
|
|
||||||
set_status: updateProjectNextItemField(input: {
|
|
||||||
projectId: $project
|
|
||||||
itemId: $item
|
|
||||||
fieldId: $status_field
|
|
||||||
value: $status_value
|
|
||||||
}) {
|
|
||||||
projectNextItem {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.6"
|
version = "0.5.16"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -16,9 +16,9 @@ rocket_impl = [
|
|||||||
"lru",
|
"lru",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
|
||||||
"rauth/database-mongodb",
|
"authifier/database-mongodb",
|
||||||
"rauth/rocket_impl",
|
"authifier/rocket_impl",
|
||||||
"rauth/okapi_impl"
|
"authifier/okapi_impl"
|
||||||
]
|
]
|
||||||
|
|
||||||
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl" ]
|
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl" ]
|
||||||
@@ -29,7 +29,7 @@ default = [ "test" ]
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
validator = { version = "0.14", features = ["derive"] }
|
validator = { version = "0.14", features = ["derive"] }
|
||||||
iso8601-timestamp = { version = "0.1.8", features = ["schema", "bson"] }
|
iso8601-timestamp = { version = "0.1.8", features = ["schema", "bson"] }
|
||||||
optional_struct = { git = "https://github.com/insertish/OptionalStruct", rev = "e275d2726595474632485934aa0887fa52281f70" }
|
optional_struct = { git = "https://github.com/insertish/OptionalStruct", rev = "ee56427cee1f007839825d93d07fffd5a5e038c7" }
|
||||||
|
|
||||||
# Formats
|
# Formats
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
@@ -69,6 +69,7 @@ reqwest = "0.11.10"
|
|||||||
bitfield = "0.13.2"
|
bitfield = "0.13.2"
|
||||||
once_cell = "1.13.0"
|
once_cell = "1.13.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
async-lock = "2.6.0"
|
||||||
|
|
||||||
lru = { version = "0.7.6", optional = true }
|
lru = { version = "0.7.6", optional = true }
|
||||||
dashmap = { version = "5.2.0", optional = true }
|
dashmap = { version = "5.2.0", optional = true }
|
||||||
@@ -83,8 +84,8 @@ rocket = { optional = true, version = "0.5.0-rc.2", default-features = false, fe
|
|||||||
rocket_empty = { version = "0.1.1", optional = true, features = [ "schema" ] }
|
rocket_empty = { version = "0.1.1", optional = true, features = [ "schema" ] }
|
||||||
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "c17e8145baa4790319fdb6a473e465b960f55e7c" }
|
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "c17e8145baa4790319fdb6a473e465b960f55e7c" }
|
||||||
|
|
||||||
# rAuth
|
# Authifier
|
||||||
rauth = { git = "https://github.com/insertish/rauth", tag = "1.0.3", features = [ "async-std-runtime" ] }
|
authifier = { version = "1.0.7", features = [ "async-std-runtime" ] }
|
||||||
|
|
||||||
# Sentry
|
# Sentry
|
||||||
sentry = "0.25.0"
|
sentry = "0.25.0"
|
||||||
|
|||||||
@@ -61,13 +61,13 @@ impl Deref for Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Database> for rauth::Database {
|
impl From<Database> for authifier::Database {
|
||||||
fn from(val: Database) -> Self {
|
fn from(val: Database) -> Self {
|
||||||
match val {
|
match val {
|
||||||
Database::Dummy(_) => rauth::Database::default(),
|
Database::Dummy(_) => authifier::Database::default(),
|
||||||
Database::MongoDb(MongoDb(client)) => {
|
Database::MongoDb(MongoDb(client)) => authifier::Database::MongoDb(
|
||||||
rauth::Database::MongoDb(rauth::database::MongoDb(client.database("revolt")))
|
authifier::database::MongoDb(client.database("revolt")),
|
||||||
}
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use rauth::RAuthEvent;
|
use authifier::AuthifierEvent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::channel::{FieldsChannel, PartialChannel};
|
use crate::models::channel::{FieldsChannel, PartialChannel};
|
||||||
@@ -6,7 +6,7 @@ use crate::models::message::{AppendMessage, PartialMessage};
|
|||||||
use crate::models::server::{FieldsRole, FieldsServer, PartialRole, PartialServer};
|
use crate::models::server::{FieldsRole, FieldsServer, PartialRole, PartialServer};
|
||||||
use crate::models::server_member::{FieldsMember, MemberCompositeKey, PartialMember};
|
use crate::models::server_member::{FieldsMember, MemberCompositeKey, PartialMember};
|
||||||
use crate::models::user::{FieldsUser, PartialUser, RelationshipStatus};
|
use crate::models::user::{FieldsUser, PartialUser, RelationshipStatus};
|
||||||
use crate::models::{Channel, Emoji, Member, Message, Server, User, UserSettings};
|
use crate::models::{Channel, Emoji, Member, Message, Report, Server, User, UserSettings};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
/// WebSocket Client Errors
|
/// WebSocket Client Errors
|
||||||
@@ -213,6 +213,9 @@ pub enum EventV1 {
|
|||||||
/// Delete emoji
|
/// Delete emoji
|
||||||
EmojiDelete { id: String },
|
EmojiDelete { id: String },
|
||||||
|
|
||||||
|
/// New report
|
||||||
|
ReportCreate(Report),
|
||||||
|
|
||||||
/// Auth events
|
/// Auth events
|
||||||
Auth(RAuthEvent),
|
Auth(AuthifierEvent),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use crate::{models::stats::Stats, AbstractStats, Result};
|
||||||
|
|
||||||
|
use super::super::DummyDb;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractStats for DummyDb {
|
||||||
|
async fn generate_stats(&self) -> Result<Stats> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::models::message::{AppendMessage, Message, MessageSort, PartialMessage};
|
use crate::models::message::{AppendMessage, Message, MessageQuery, PartialMessage};
|
||||||
use crate::{AbstractMessage, Result};
|
use crate::{AbstractMessage, Result};
|
||||||
|
|
||||||
use super::super::DummyDb;
|
use super::super::DummyDb;
|
||||||
@@ -41,28 +41,8 @@ impl AbstractMessage for DummyDb {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_messages(
|
async fn fetch_messages(&self, _query: MessageQuery) -> Result<Vec<Message>> {
|
||||||
&self,
|
Ok(vec![])
|
||||||
channel: &str,
|
|
||||||
_limit: Option<i64>,
|
|
||||||
_before: Option<String>,
|
|
||||||
_after: Option<String>,
|
|
||||||
_sort: Option<MessageSort>,
|
|
||||||
_nearby: Option<String>,
|
|
||||||
) -> Result<Vec<Message>> {
|
|
||||||
Ok(vec![self.fetch_message(channel).await.unwrap()])
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn search_messages(
|
|
||||||
&self,
|
|
||||||
channel: &str,
|
|
||||||
_query: &str,
|
|
||||||
_limit: Option<i64>,
|
|
||||||
_before: Option<String>,
|
|
||||||
_after: Option<String>,
|
|
||||||
_sort: MessageSort,
|
|
||||||
) -> Result<Vec<Message>> {
|
|
||||||
Ok(vec![self.fetch_message(channel).await.unwrap()])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new reaction to a message
|
/// Add a new reaction to a message
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use crate::AbstractDatabase;
|
|||||||
|
|
||||||
pub mod admin {
|
pub mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod media {
|
pub mod media {
|
||||||
@@ -28,6 +29,11 @@ pub mod users {
|
|||||||
pub mod user_settings;
|
pub mod user_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod safety {
|
||||||
|
pub mod report;
|
||||||
|
pub mod snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DummyDb;
|
pub struct DummyDb;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use crate::models::report::PartialReport;
|
||||||
|
use crate::models::Report;
|
||||||
|
use crate::{AbstractReport, Result};
|
||||||
|
|
||||||
|
use super::super::DummyDb;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractReport for DummyDb {
|
||||||
|
async fn insert_report(&self, report: &Report) -> Result<()> {
|
||||||
|
info!("Insert {:?}", report);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_report(&self, _id: &str, _report: &PartialReport) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_report(&self, _report_id: &str) -> Result<Report> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_reports(&self) -> Result<Vec<Report>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
use crate::models::Snapshot;
|
||||||
|
use crate::{AbstractSnapshot, Result};
|
||||||
|
|
||||||
|
use super::super::DummyDb;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractSnapshot for DummyDb {
|
||||||
|
async fn insert_snapshot(&self, snapshot: &Snapshot) -> Result<()> {
|
||||||
|
info!("Insert {:?}", snapshot);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_snapshot(&self, _report_id: &str) -> Result<Snapshot> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -377,7 +377,7 @@ impl SendableEmbed {
|
|||||||
impl BulkMessageResponse {
|
impl BulkMessageResponse {
|
||||||
pub async fn transform(
|
pub async fn transform(
|
||||||
db: &Database,
|
db: &Database,
|
||||||
channel: &Channel,
|
channel: Option<&Channel>,
|
||||||
messages: Vec<Message>,
|
messages: Vec<Message>,
|
||||||
include_users: Option<bool>,
|
include_users: Option<bool>,
|
||||||
) -> Result<BulkMessageResponse> {
|
) -> Result<BulkMessageResponse> {
|
||||||
@@ -386,7 +386,8 @@ impl BulkMessageResponse {
|
|||||||
let users = User::fetch_foreign_users(db, &user_ids).await?;
|
let users = User::fetch_foreign_users(db, &user_ids).await?;
|
||||||
|
|
||||||
Ok(match channel {
|
Ok(match channel {
|
||||||
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
|
Some(Channel::TextChannel { server, .. })
|
||||||
|
| Some(Channel::VoiceChannel { server, .. }) => {
|
||||||
BulkMessageResponse::MessagesAndUsers {
|
BulkMessageResponse::MessagesAndUsers {
|
||||||
messages,
|
messages,
|
||||||
users,
|
users,
|
||||||
|
|||||||
@@ -27,3 +27,7 @@ pub mod users {
|
|||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod user_settings;
|
pub mod user_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod safety {
|
||||||
|
pub mod report;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use crate::{models::report::PartialReport, models::Report, Database, Result};
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
/// Update report data
|
||||||
|
pub async fn update(&mut self, db: &Database, partial: PartialReport) -> Result<()> {
|
||||||
|
self.apply_options(partial.clone());
|
||||||
|
db.update_report(&self.id, &partial).await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,6 +57,14 @@ pub async fn create_database(db: &MongoDb) {
|
|||||||
.await
|
.await
|
||||||
.expect("Failed to create user_settings collection.");
|
.expect("Failed to create user_settings collection.");
|
||||||
|
|
||||||
|
db.create_collection("safety_reports", None)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create safety_reports collection.");
|
||||||
|
|
||||||
|
db.create_collection("safety_snapshots", None)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create safety_snapshots collection.");
|
||||||
|
|
||||||
db.create_collection("bots", None)
|
db.create_collection("bots", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create bots collection.");
|
.expect("Failed to create bots collection.");
|
||||||
@@ -103,18 +111,18 @@ pub async fn create_database(db: &MongoDb) {
|
|||||||
},
|
},
|
||||||
"name": "content"
|
"name": "content"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"key": {
|
|
||||||
"channel": 1_i32
|
|
||||||
},
|
|
||||||
"name": "channel"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": {
|
"key": {
|
||||||
"channel": 1_i32,
|
"channel": 1_i32,
|
||||||
"_id": 1_i32
|
"_id": 1_i32
|
||||||
},
|
},
|
||||||
"name": "channel_id_compound"
|
"name": "channel_id_compound"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"author": 1_i32
|
||||||
|
},
|
||||||
|
"name": "author"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ struct MigrationInfo {
|
|||||||
revision: i32,
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 18;
|
pub const LATEST_REVISION: i32 = 21;
|
||||||
|
|
||||||
pub async fn migrate_database(db: &MongoDb) {
|
pub async fn migrate_database(db: &MongoDb) {
|
||||||
let migrations = db.col::<Document>("migrations");
|
let migrations = db.col::<Document>("migrations");
|
||||||
@@ -215,7 +215,7 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if revision <= 8 {
|
if revision <= 8 {
|
||||||
info!("Running migration [revision 8 / 2021-09-10]: Update to rAuth version 1.");
|
info!("Running migration [revision 8 / 2021-09-10]: Update to Authifier version 1.");
|
||||||
|
|
||||||
db.db()
|
db.db()
|
||||||
.run_command(
|
.run_command(
|
||||||
@@ -603,20 +603,20 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if revision <= 15 {
|
if revision <= 15 {
|
||||||
info!("Running migration [revision 15 / 04-06-2022]: Migrate rAuth to latest version.");
|
info!("Running migration [revision 15 / 04-06-2022]: Migrate Authifier to latest version.");
|
||||||
|
|
||||||
let db = rauth::Database::MongoDb(rauth::database::MongoDb(db.db()));
|
let db = authifier::Database::MongoDb(authifier::database::MongoDb(db.db()));
|
||||||
db.run_migration(rauth::Migration::M2022_06_03EnsureUpToSpec)
|
db.run_migration(authifier::Migration::M2022_06_03EnsureUpToSpec)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if revision <= 16 {
|
if revision <= 16 {
|
||||||
info!("Running migration [revision 16 / 07-07-2022]: Add `emojis` collection and rAuth migration.");
|
info!("Running migration [revision 16 / 07-07-2022]: Add `emojis` collection and Authifier migration.");
|
||||||
|
|
||||||
let rauth_db = rauth::Database::MongoDb(rauth::database::MongoDb(db.db()));
|
let authifier_db = authifier::Database::MongoDb(authifier::database::MongoDb(db.db()));
|
||||||
rauth_db
|
authifier_db
|
||||||
.run_migration(rauth::Migration::M2022_06_09AddIndexForDeletion)
|
.run_migration(authifier::Migration::M2022_06_09AddIndexForDeletion)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -661,6 +661,103 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
.expect("Failed to update server members.");
|
.expect("Failed to update server members.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if revision <= 18 {
|
||||||
|
info!("Running migration [revision 18 / 27-02-2022]: Create author index on messages. Drop plain channel index if exists.");
|
||||||
|
|
||||||
|
if db
|
||||||
|
.db()
|
||||||
|
.run_command(
|
||||||
|
doc! {
|
||||||
|
"dropIndexes": "messages",
|
||||||
|
"index": ["channel"]
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
info!("Failed to drop `messages.channel` index but this is ok since that means it's probably gone.");
|
||||||
|
}
|
||||||
|
|
||||||
|
db.db()
|
||||||
|
.run_command(
|
||||||
|
doc! {
|
||||||
|
"createIndexes": "messages",
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"author": 1_i32,
|
||||||
|
},
|
||||||
|
"name": "author"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create messages author index.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if revision <= 19 {
|
||||||
|
info!("Running migration [revision 19 / 27-02-2023]: Create report / snapshot collections, migrate to new model if applicable.");
|
||||||
|
|
||||||
|
// TODO: make these fail once production is migrated
|
||||||
|
if db
|
||||||
|
.db()
|
||||||
|
.create_collection("safety_reports", None)
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
info!("Failed to create safety_reports collection but this is expected in production.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if db
|
||||||
|
.db()
|
||||||
|
.create_collection("safety_snapshots", None)
|
||||||
|
.await
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
"Failed to create safety_snapshots collection but this is expected in production."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.col::<Document>("safety_reports")
|
||||||
|
.update_many(
|
||||||
|
doc! {},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"status": "Created"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if revision <= 20 {
|
||||||
|
info!("Running migration [revision 20 / 28-02-2023]: Add index `snapshot.report_id`.");
|
||||||
|
|
||||||
|
db.db()
|
||||||
|
.run_command(
|
||||||
|
doc! {
|
||||||
|
"createIndexes": "safety_snapshots",
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"report_id": 1_i32
|
||||||
|
},
|
||||||
|
"name": "report_id"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create safety snapshot index.");
|
||||||
|
}
|
||||||
|
|
||||||
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
// Need to migrate fields on attachments, change `user_id`, `object_id`, etc to `parent`.
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use bson::{from_document, Document};
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
models::stats::{Index, Stats},
|
||||||
|
AbstractStats, Error, Result,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::MongoDb;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractStats for MongoDb {
|
||||||
|
async fn generate_stats(&self) -> Result<Stats> {
|
||||||
|
let mut indices = HashMap::new();
|
||||||
|
let mut coll_stats = HashMap::new();
|
||||||
|
|
||||||
|
let collection_names =
|
||||||
|
self.db()
|
||||||
|
.list_collection_names(None)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "list_collection_names",
|
||||||
|
with: "database",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for collection in collection_names {
|
||||||
|
indices.insert(
|
||||||
|
collection.to_string(),
|
||||||
|
self.col::<Document>(&collection)
|
||||||
|
.aggregate(
|
||||||
|
vec![doc! {
|
||||||
|
"$indexStats": { }
|
||||||
|
}],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "aggregate",
|
||||||
|
with: "col",
|
||||||
|
})?
|
||||||
|
.filter_map(|s| async { s.ok() })
|
||||||
|
.collect::<Vec<Document>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|doc| from_document(doc).ok())
|
||||||
|
.collect::<Vec<Index>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
coll_stats.insert(
|
||||||
|
collection.to_string(),
|
||||||
|
self.col::<Document>(&collection)
|
||||||
|
.aggregate(
|
||||||
|
vec![doc! {
|
||||||
|
"$collStats": {
|
||||||
|
"latencyStats": {
|
||||||
|
"histograms": true
|
||||||
|
},
|
||||||
|
"storageStats": {},
|
||||||
|
"count": {},
|
||||||
|
"queryExecStats": {}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "aggregate",
|
||||||
|
with: "col",
|
||||||
|
})?
|
||||||
|
.filter_map(|s| async { s.ok() })
|
||||||
|
.collect::<Vec<Document>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|doc| from_document(doc).ok())
|
||||||
|
.next()
|
||||||
|
.ok_or(Error::DatabaseError {
|
||||||
|
operation: "next aggregation",
|
||||||
|
with: "col",
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Stats {
|
||||||
|
indices,
|
||||||
|
coll_stats,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,9 @@ use bson::{to_bson, Document};
|
|||||||
use futures::try_join;
|
use futures::try_join;
|
||||||
use mongodb::options::FindOptions;
|
use mongodb::options::FindOptions;
|
||||||
|
|
||||||
use crate::models::message::{AppendMessage, Message, MessageSort, PartialMessage};
|
use crate::models::message::{
|
||||||
|
AppendMessage, Message, MessageQuery, MessageSort, MessageTimePeriod, PartialMessage,
|
||||||
|
};
|
||||||
use crate::r#impl::mongo::DocumentId;
|
use crate::r#impl::mongo::DocumentId;
|
||||||
use crate::{AbstractMessage, Error, Result};
|
use crate::{AbstractMessage, Error, Result};
|
||||||
|
|
||||||
@@ -139,145 +141,138 @@ impl AbstractMessage for MongoDb {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_messages(
|
async fn fetch_messages(&self, query: MessageQuery) -> Result<Vec<Message>> {
|
||||||
&self,
|
let mut filter = doc! {};
|
||||||
channel: &str,
|
|
||||||
limit: Option<i64>,
|
// 1. Apply message filters
|
||||||
before: Option<String>,
|
if let Some(channel) = query.filter.channel {
|
||||||
after: Option<String>,
|
filter.insert("channel", channel);
|
||||||
sort: Option<MessageSort>,
|
}
|
||||||
nearby: Option<String>,
|
|
||||||
) -> Result<Vec<Message>> {
|
if let Some(author) = query.filter.author {
|
||||||
let limit = limit.unwrap_or(50);
|
filter.insert("author", author);
|
||||||
Ok(if let Some(nearby) = nearby {
|
}
|
||||||
let (a, b) = try_join!(
|
|
||||||
self.find_with_options::<_, Message>(
|
let is_search_query = if let Some(query) = query.filter.query {
|
||||||
COL,
|
filter.insert(
|
||||||
|
"$text",
|
||||||
|
doc! {
|
||||||
|
"$search": query
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Find query limit
|
||||||
|
let limit = query.limit.unwrap_or(50);
|
||||||
|
|
||||||
|
// 3. Apply message time period
|
||||||
|
match query.time_period {
|
||||||
|
MessageTimePeriod::Relative { nearby } => {
|
||||||
|
// 3.1. Prepare filters
|
||||||
|
let mut older_message_filter = filter.clone();
|
||||||
|
let mut newer_message_filter = filter;
|
||||||
|
|
||||||
|
older_message_filter.insert(
|
||||||
|
"_id",
|
||||||
doc! {
|
doc! {
|
||||||
"channel": channel,
|
"$lt": &nearby
|
||||||
"_id": {
|
|
||||||
"$gte": &nearby
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
FindOptions::builder()
|
);
|
||||||
.limit(limit / 2 + 1)
|
|
||||||
.sort(doc! {
|
newer_message_filter.insert(
|
||||||
"_id": 1_i32
|
"_id",
|
||||||
})
|
|
||||||
.build(),
|
|
||||||
),
|
|
||||||
self.find_with_options::<_, Message>(
|
|
||||||
COL,
|
|
||||||
doc! {
|
doc! {
|
||||||
"channel": channel,
|
"$gte": &nearby
|
||||||
"_id": {
|
|
||||||
"$lt": &nearby
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3.2. Execute in both directions
|
||||||
|
let (a, b) = try_join!(
|
||||||
|
self.find_with_options::<_, Message>(
|
||||||
|
COL,
|
||||||
|
newer_message_filter,
|
||||||
|
FindOptions::builder()
|
||||||
|
.limit(limit / 2 + 1)
|
||||||
|
.sort(doc! {
|
||||||
|
"_id": 1_i32
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
self.find_with_options::<_, Message>(
|
||||||
|
COL,
|
||||||
|
older_message_filter,
|
||||||
|
FindOptions::builder()
|
||||||
|
.limit(limit / 2)
|
||||||
|
.sort(doc! {
|
||||||
|
"_id": -1_i32
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok([a, b].concat())
|
||||||
|
}
|
||||||
|
MessageTimePeriod::Absolute {
|
||||||
|
before,
|
||||||
|
after,
|
||||||
|
sort,
|
||||||
|
} => {
|
||||||
|
// 3.1. Apply message ID filter
|
||||||
|
if let Some(doc) = match (before, after) {
|
||||||
|
(Some(before), Some(after)) => Some(doc! {
|
||||||
|
"$lt": before,
|
||||||
|
"$gt": after
|
||||||
|
}),
|
||||||
|
(Some(before), _) => Some(doc! {
|
||||||
|
"$lt": before
|
||||||
|
}),
|
||||||
|
(_, Some(after)) => Some(doc! {
|
||||||
|
"$gt": after
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
} {
|
||||||
|
filter.insert("_id", doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.2. Execute with given message sort
|
||||||
|
self.find_with_options(
|
||||||
|
COL,
|
||||||
|
filter,
|
||||||
FindOptions::builder()
|
FindOptions::builder()
|
||||||
.limit(limit / 2)
|
.limit(limit)
|
||||||
.sort(doc! {
|
.sort(match sort.unwrap_or(MessageSort::Latest) {
|
||||||
"_id": -1_i32
|
// Sort by relevance, fallback to latest
|
||||||
|
MessageSort::Relevance => {
|
||||||
|
if is_search_query {
|
||||||
|
doc! {
|
||||||
|
"score": {
|
||||||
|
"$meta": "textScore"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
doc! {
|
||||||
|
"_id": -1_i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort by latest first
|
||||||
|
MessageSort::Latest => doc! {
|
||||||
|
"_id": -1_i32
|
||||||
|
},
|
||||||
|
// Sort by oldest first
|
||||||
|
MessageSort::Oldest => doc! {
|
||||||
|
"_id": 1_i32
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
)?;
|
.await
|
||||||
|
|
||||||
[a, b].concat()
|
|
||||||
} else {
|
|
||||||
let mut query = doc! { "channel": channel };
|
|
||||||
if let Some(before) = before {
|
|
||||||
query.insert("_id", doc! { "$lt": before });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(after) = after {
|
|
||||||
query.insert("_id", doc! { "$gt": after });
|
|
||||||
}
|
|
||||||
|
|
||||||
let sort: i32 = if let MessageSort::Latest = sort.unwrap_or(MessageSort::Latest) {
|
|
||||||
-1
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
self.find_with_options::<_, Message>(
|
|
||||||
COL,
|
|
||||||
query,
|
|
||||||
FindOptions::builder()
|
|
||||||
.limit(limit)
|
|
||||||
.sort(doc! {
|
|
||||||
"_id": sort
|
|
||||||
})
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn search_messages(
|
|
||||||
&self,
|
|
||||||
channel: &str,
|
|
||||||
query: &str,
|
|
||||||
limit: Option<i64>,
|
|
||||||
before: Option<String>,
|
|
||||||
after: Option<String>,
|
|
||||||
sort: MessageSort,
|
|
||||||
) -> Result<Vec<Message>> {
|
|
||||||
let limit = limit.unwrap_or(50);
|
|
||||||
|
|
||||||
let mut filter = doc! {
|
|
||||||
"channel": channel,
|
|
||||||
"$text": {
|
|
||||||
"$search": query
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(doc) = match (before, after) {
|
|
||||||
(Some(before), Some(after)) => Some(doc! {
|
|
||||||
"lt": before,
|
|
||||||
"gt": after
|
|
||||||
}),
|
|
||||||
(Some(before), _) => Some(doc! {
|
|
||||||
"lt": before
|
|
||||||
}),
|
|
||||||
(_, Some(after)) => Some(doc! {
|
|
||||||
"gt": after
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
} {
|
|
||||||
filter.insert("_id", doc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.find_with_options(
|
|
||||||
COL,
|
|
||||||
filter,
|
|
||||||
FindOptions::builder()
|
|
||||||
.projection(if let MessageSort::Relevance = &sort {
|
|
||||||
doc! {
|
|
||||||
"score": {
|
|
||||||
"$meta": "textScore"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
doc! {}
|
|
||||||
})
|
|
||||||
.limit(limit)
|
|
||||||
.sort(match &sort {
|
|
||||||
MessageSort::Relevance => doc! {
|
|
||||||
"score": {
|
|
||||||
"$meta": "textScore"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MessageSort::Latest => doc! {
|
|
||||||
"_id": -1_i32
|
|
||||||
},
|
|
||||||
MessageSort::Oldest => doc! {
|
|
||||||
"_id": 1_i32
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new reaction to a message
|
/// Add a new reaction to a message
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::{util::manipulation::prefix_keys, AbstractDatabase, Error, Result};
|
|||||||
|
|
||||||
pub mod admin {
|
pub mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod media {
|
pub mod media {
|
||||||
@@ -39,6 +40,11 @@ pub mod users {
|
|||||||
pub mod user_settings;
|
pub mod user_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod safety {
|
||||||
|
pub mod report;
|
||||||
|
pub mod snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MongoDb(pub mongodb::Client);
|
pub struct MongoDb(pub mongodb::Client);
|
||||||
|
|
||||||
@@ -84,17 +90,25 @@ impl MongoDb {
|
|||||||
where
|
where
|
||||||
O: Into<Option<FindOptions>>,
|
O: Into<Option<FindOptions>>,
|
||||||
{
|
{
|
||||||
Ok(self
|
let result = self.col::<T>(collection).find(projection, options).await;
|
||||||
.col::<T>(collection)
|
Ok(if cfg!(debug_assertions) {
|
||||||
.find(projection, options)
|
result.unwrap()
|
||||||
.await
|
} else {
|
||||||
.map_err(|_| Error::DatabaseError {
|
result.map_err(|_| Error::DatabaseError {
|
||||||
operation: "find",
|
operation: "find",
|
||||||
with: collection,
|
with: collection,
|
||||||
})?
|
})?
|
||||||
.filter_map(|s| async { s.ok() })
|
}
|
||||||
.collect::<Vec<T>>()
|
.filter_map(|s| async {
|
||||||
.await)
|
if cfg!(debug_assertions) {
|
||||||
|
// Hard fail on invalid documents
|
||||||
|
Some(s.unwrap())
|
||||||
|
} else {
|
||||||
|
s.ok()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<T>>()
|
||||||
|
.await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find<T: DeserializeOwned + Unpin + Send + Sync>(
|
async fn find<T: DeserializeOwned + Unpin + Send + Sync>(
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use crate::models::report::PartialReport;
|
||||||
|
use crate::models::Report;
|
||||||
|
use crate::{AbstractReport, Result};
|
||||||
|
|
||||||
|
use super::super::MongoDb;
|
||||||
|
|
||||||
|
static COL: &str = "safety_reports";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractReport for MongoDb {
|
||||||
|
async fn insert_report(&self, report: &Report) -> Result<()> {
|
||||||
|
self.insert_one(COL, report).await.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_report(&self, id: &str, report: &PartialReport) -> Result<()> {
|
||||||
|
self.update_one_by_id(COL, id, report, vec![], None)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_report(&self, report_id: &str) -> Result<Report> {
|
||||||
|
self.find_one_by_id(COL, report_id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_reports(&self) -> Result<Vec<Report>> {
|
||||||
|
self.find(COL, doc! {}).await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
use crate::models::Snapshot;
|
||||||
|
use crate::{AbstractSnapshot, Result};
|
||||||
|
|
||||||
|
use super::super::MongoDb;
|
||||||
|
|
||||||
|
static COL: &str = "safety_snapshots";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractSnapshot for MongoDb {
|
||||||
|
async fn insert_snapshot(&self, snapshot: &Snapshot) -> Result<()> {
|
||||||
|
self.insert_one(COL, snapshot).await.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_snapshot(&self, report_id: &str) -> Result<Snapshot> {
|
||||||
|
self.find_one(
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"report_id": report_id
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use rauth::models::Session;
|
use authifier::models::Session;
|
||||||
use revolt_okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
use revolt_okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
||||||
use revolt_rocket_okapi::gen::OpenApiGenerator;
|
use revolt_rocket_okapi::gen::OpenApiGenerator;
|
||||||
use revolt_rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
use revolt_rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
||||||
@@ -12,7 +12,7 @@ use crate::Database;
|
|||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for User {
|
impl<'r> FromRequest<'r> for User {
|
||||||
type Error = rauth::Error;
|
type Error = authifier::Error;
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
let user: &Option<User> = request
|
let user: &Option<User> = request
|
||||||
@@ -43,7 +43,7 @@ impl<'r> FromRequest<'r> for User {
|
|||||||
if let Some(user) = user {
|
if let Some(user) = user {
|
||||||
Outcome::Success(user.clone())
|
Outcome::Success(user.clone())
|
||||||
} else {
|
} else {
|
||||||
Outcome::Failure((Status::Unauthorized, rauth::Error::InvalidSession))
|
Outcome::Failure((Status::Unauthorized, authifier::Error::InvalidSession))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ extern crate bitfield;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bson;
|
extern crate bson;
|
||||||
|
|
||||||
|
pub use authifier;
|
||||||
pub use iso8601_timestamp::Timestamp;
|
pub use iso8601_timestamp::Timestamp;
|
||||||
pub use rauth;
|
|
||||||
pub use redis_kiss;
|
pub use redis_kiss;
|
||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Index access information
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct IndexAccess {
|
||||||
|
/// Operations since timestamp
|
||||||
|
ops: i32,
|
||||||
|
|
||||||
|
/// Timestamp at which data keeping begun
|
||||||
|
since: Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection index
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct Index {
|
||||||
|
/// Index name
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
/// Access information
|
||||||
|
accesses: IndexAccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Histogram entry
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct LatencyHistogramEntry {
|
||||||
|
/// Time
|
||||||
|
micros: i64,
|
||||||
|
|
||||||
|
/// Count
|
||||||
|
count: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection latency stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct LatencyStats {
|
||||||
|
/// Total operations
|
||||||
|
ops: i64,
|
||||||
|
|
||||||
|
/// Timestamp at which data keeping begun
|
||||||
|
latency: i64,
|
||||||
|
|
||||||
|
/// Histogram representation of latency data
|
||||||
|
histogram: Vec<LatencyHistogramEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection storage stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct StorageStats {
|
||||||
|
/// Uncompressed data size
|
||||||
|
size: i64,
|
||||||
|
|
||||||
|
/// Data size on disk
|
||||||
|
storage_size: i64,
|
||||||
|
|
||||||
|
/// Total size of all indexes
|
||||||
|
total_index_size: i64,
|
||||||
|
|
||||||
|
/// Sum of storage size and total index size
|
||||||
|
total_size: i64,
|
||||||
|
|
||||||
|
/// Individual index sizes
|
||||||
|
index_sizes: HashMap<String, i64>,
|
||||||
|
|
||||||
|
/// Number of documents in collection
|
||||||
|
count: i64,
|
||||||
|
|
||||||
|
/// Average size of each document
|
||||||
|
avg_obj_size: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query collection scan stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CollectionScans {
|
||||||
|
/// Number of total collection scans
|
||||||
|
total: i64,
|
||||||
|
|
||||||
|
/// Number of total collection scans not using a tailable cursor
|
||||||
|
non_tailable: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection query execution stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct QueryExecStats {
|
||||||
|
/// Stats regarding collection scans
|
||||||
|
collection_scans: CollectionScans,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CollectionStats {
|
||||||
|
/// Namespace
|
||||||
|
ns: String,
|
||||||
|
|
||||||
|
/// Local time
|
||||||
|
local_time: Timestamp,
|
||||||
|
|
||||||
|
/// Latency stats
|
||||||
|
latency_stats: HashMap<String, LatencyStats>,
|
||||||
|
|
||||||
|
/// Query exec stats
|
||||||
|
query_exec_stats: QueryExecStats,
|
||||||
|
|
||||||
|
/// Number of documents in collection
|
||||||
|
count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Server Stats
|
||||||
|
#[derive(Serialize, JsonSchema, Debug)]
|
||||||
|
pub struct Stats {
|
||||||
|
/// Index usage information
|
||||||
|
pub indices: HashMap<String, Vec<Index>>,
|
||||||
|
|
||||||
|
/// Collection stats
|
||||||
|
pub coll_stats: HashMap<String, CollectionStats>,
|
||||||
|
}
|
||||||
@@ -98,6 +98,8 @@ pub struct Interactions {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
pub reactions: Option<IndexSet<String>>,
|
pub reactions: Option<IndexSet<String>>,
|
||||||
/// Whether reactions should be restricted to the given list
|
/// Whether reactions should be restricted to the given list
|
||||||
|
///
|
||||||
|
/// Can only be set to true if reactions list is of at least length 1
|
||||||
#[serde(skip_serializing_if = "if_false", default)]
|
#[serde(skip_serializing_if = "if_false", default)]
|
||||||
pub restrict_reactions: bool,
|
pub restrict_reactions: bool,
|
||||||
}
|
}
|
||||||
@@ -155,7 +157,7 @@ pub struct Message {
|
|||||||
/// # Message Sort
|
/// # Message Sort
|
||||||
///
|
///
|
||||||
/// Sort used for retrieving messages
|
/// Sort used for retrieving messages
|
||||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
#[cfg_attr(feature = "rocket_impl", derive(FromFormField))]
|
#[cfg_attr(feature = "rocket_impl", derive(FromFormField))]
|
||||||
pub enum MessageSort {
|
pub enum MessageSort {
|
||||||
/// Sort by the most relevant messages
|
/// Sort by the most relevant messages
|
||||||
@@ -172,6 +174,56 @@ impl Default for MessageSort {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Message Time Period
|
||||||
|
///
|
||||||
|
/// Filter and sort messages by time
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum MessageTimePeriod {
|
||||||
|
Relative {
|
||||||
|
/// Message id to search around
|
||||||
|
///
|
||||||
|
/// Specifying 'nearby' ignores 'before', 'after' and 'sort'.
|
||||||
|
/// It will also take half of limit rounded as the limits to each side.
|
||||||
|
/// It also fetches the message ID specified.
|
||||||
|
nearby: String,
|
||||||
|
},
|
||||||
|
Absolute {
|
||||||
|
/// Message id before which messages should be fetched
|
||||||
|
before: Option<String>,
|
||||||
|
/// Message id after which messages should be fetched
|
||||||
|
after: Option<String>,
|
||||||
|
/// Message sort direction
|
||||||
|
sort: Option<MessageSort>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Message Filter
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Default)]
|
||||||
|
pub struct MessageFilter {
|
||||||
|
/// Parent channel ID
|
||||||
|
pub channel: Option<String>,
|
||||||
|
/// Message author ID
|
||||||
|
pub author: Option<String>,
|
||||||
|
/// Search query
|
||||||
|
pub query: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Message Query
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct MessageQuery {
|
||||||
|
/// Maximum number of messages to fetch
|
||||||
|
///
|
||||||
|
/// For fetching nearby messages, this is \`(limit + 1)\`.
|
||||||
|
pub limit: Option<i64>,
|
||||||
|
/// Filter to apply
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub filter: MessageFilter,
|
||||||
|
/// Time period to fetch
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub time_period: MessageTimePeriod,
|
||||||
|
}
|
||||||
|
|
||||||
/// # Bulk Message Response
|
/// # Bulk Message Response
|
||||||
///
|
///
|
||||||
/// Response used when multiple messages are fetched
|
/// Response used when multiple messages are fetched
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
mod admin {
|
mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
pub mod simple;
|
pub mod simple;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod media {
|
mod media {
|
||||||
@@ -27,9 +28,15 @@ mod users {
|
|||||||
pub mod user_settings;
|
pub mod user_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod safety {
|
||||||
|
pub mod report;
|
||||||
|
pub mod snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
pub use admin::*;
|
pub use admin::*;
|
||||||
pub use channels::*;
|
pub use channels::*;
|
||||||
pub use media::*;
|
pub use media::*;
|
||||||
|
pub use safety::*;
|
||||||
pub use servers::*;
|
pub use servers::*;
|
||||||
pub use users::*;
|
pub use users::*;
|
||||||
|
|
||||||
@@ -41,9 +48,11 @@ pub use channel_unread::ChannelUnread;
|
|||||||
pub use emoji::Emoji;
|
pub use emoji::Emoji;
|
||||||
pub use message::Message;
|
pub use message::Message;
|
||||||
pub use migrations::MigrationInfo;
|
pub use migrations::MigrationInfo;
|
||||||
|
pub use report::Report;
|
||||||
pub use server::Server;
|
pub use server::Server;
|
||||||
pub use server_ban::ServerBan;
|
pub use server_ban::ServerBan;
|
||||||
pub use server_member::Member;
|
pub use server_member::Member;
|
||||||
pub use simple::SimpleModel;
|
pub use simple::SimpleModel;
|
||||||
|
pub use snapshot::Snapshot;
|
||||||
pub use user::User;
|
pub use user::User;
|
||||||
pub use user_settings::UserSettings;
|
pub use user_settings::UserSettings;
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Reason for reporting content (message or server)
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
pub enum ContentReportReason {
|
||||||
|
/// No reason has been specified
|
||||||
|
NoneSpecified,
|
||||||
|
|
||||||
|
/// Blatantly illegal content
|
||||||
|
Illegal,
|
||||||
|
|
||||||
|
/// Content that promotes harm to others / self
|
||||||
|
PromotesHarm,
|
||||||
|
|
||||||
|
/// Spam or platform abuse
|
||||||
|
SpamAbuse,
|
||||||
|
|
||||||
|
/// Distribution of malware
|
||||||
|
Malware,
|
||||||
|
|
||||||
|
/// Harassment or abuse targeted at another user
|
||||||
|
Harassment,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reason for reporting a user
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
pub enum UserReportReason {
|
||||||
|
/// No reason has been specified
|
||||||
|
NoneSpecified,
|
||||||
|
|
||||||
|
/// User is sending spam or otherwise abusing the platform
|
||||||
|
SpamAbuse,
|
||||||
|
|
||||||
|
/// User's profile contains inappropriate content for a general audience
|
||||||
|
InappropriateProfile,
|
||||||
|
|
||||||
|
/// User is impersonating another user
|
||||||
|
Impersonation,
|
||||||
|
|
||||||
|
/// User is evading a ban
|
||||||
|
BanEvasion,
|
||||||
|
|
||||||
|
/// User is not of minimum age to use the platform
|
||||||
|
Underage,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The content being reported
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum ReportedContent {
|
||||||
|
/// Report a message
|
||||||
|
Message {
|
||||||
|
/// ID of the message
|
||||||
|
id: String,
|
||||||
|
/// Reason for reporting message
|
||||||
|
report_reason: ContentReportReason,
|
||||||
|
},
|
||||||
|
/// Report a server
|
||||||
|
Server {
|
||||||
|
/// ID of the server
|
||||||
|
id: String,
|
||||||
|
/// Reason for reporting server
|
||||||
|
report_reason: ContentReportReason,
|
||||||
|
},
|
||||||
|
/// Report a user
|
||||||
|
User {
|
||||||
|
/// ID of the user
|
||||||
|
id: String,
|
||||||
|
/// Reason for reporting a user
|
||||||
|
report_reason: UserReportReason,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Status of the report
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
|
||||||
|
#[serde(tag = "status")]
|
||||||
|
pub enum ReportStatus {
|
||||||
|
/// Report is waiting for triage / action
|
||||||
|
Created {},
|
||||||
|
|
||||||
|
/// Report was rejected
|
||||||
|
Rejected { rejection_reason: String },
|
||||||
|
|
||||||
|
/// Report was actioned and resolved
|
||||||
|
Resolved {},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// User-generated platform moderation report.
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug, OptionalStruct, Clone)]
|
||||||
|
#[optional_derive(Serialize, Deserialize, JsonSchema, Debug, Default, Clone)]
|
||||||
|
#[optional_name = "PartialReport"]
|
||||||
|
#[opt_skip_serializing_none]
|
||||||
|
pub struct Report {
|
||||||
|
/// Unique Id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: String,
|
||||||
|
/// Id of the user creating this report
|
||||||
|
pub author_id: String,
|
||||||
|
/// Reported content
|
||||||
|
pub content: ReportedContent,
|
||||||
|
/// Additional report context
|
||||||
|
pub additional_context: String,
|
||||||
|
/// Status of the report
|
||||||
|
#[opt_passthrough]
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub status: ReportStatus,
|
||||||
|
/// Additional notes included on the report
|
||||||
|
#[serde(default)]
|
||||||
|
pub notes: String,
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::models::{Channel, Message, Server, User};
|
||||||
|
|
||||||
|
/// Enum to map into different models
|
||||||
|
/// that can be saved in a snapshot
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(tag = "_type")]
|
||||||
|
pub enum SnapshotContent {
|
||||||
|
Message {
|
||||||
|
/// Context before the message
|
||||||
|
#[serde(rename = "_prior_context", default)]
|
||||||
|
prior_context: Vec<Message>,
|
||||||
|
|
||||||
|
/// Context after the message
|
||||||
|
#[serde(rename = "_leading_context", default)]
|
||||||
|
leading_context: Vec<Message>,
|
||||||
|
|
||||||
|
/// Message
|
||||||
|
#[serde(flatten)]
|
||||||
|
message: Message,
|
||||||
|
},
|
||||||
|
Server(Server),
|
||||||
|
User(User),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Snapshot of some content
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct Snapshot {
|
||||||
|
/// Unique Id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: String,
|
||||||
|
/// Report parent Id
|
||||||
|
pub report_id: String,
|
||||||
|
/// Snapshot of content
|
||||||
|
pub content: SnapshotContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Snapshot of some content with required data to render
|
||||||
|
#[derive(Serialize, JsonSchema, Debug)]
|
||||||
|
pub struct SnapshotWithContext {
|
||||||
|
/// Snapshot itself
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub snapshot: Snapshot,
|
||||||
|
/// Users involved in snapshot
|
||||||
|
#[serde(rename = "_users", skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub users: Vec<User>,
|
||||||
|
/// Channels involved in snapshot
|
||||||
|
#[serde(rename = "_channels", skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub channels: Vec<Channel>,
|
||||||
|
/// Server involved in snapshot
|
||||||
|
#[serde(rename = "_server", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub server: Option<Server>,
|
||||||
|
}
|
||||||
@@ -118,7 +118,7 @@ pub struct Server {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub banner: Option<File>,
|
pub banner: Option<File>,
|
||||||
|
|
||||||
/// Enum of server flags
|
/// Bitfield of server flags
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub flags: Option<i32>,
|
pub flags: Option<i32>,
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus {
|
|||||||
async fn calculate_permission(data: &mut PermissionCalculator<'_>, db: &crate::Database) -> u32 {
|
async fn calculate_permission(data: &mut PermissionCalculator<'_>, db: &crate::Database) -> u32 {
|
||||||
let user = data.user.get().unwrap();
|
let user = data.user.get().unwrap();
|
||||||
|
|
||||||
|
if data.perspective.privileged {
|
||||||
|
return u32::MAX;
|
||||||
|
}
|
||||||
|
|
||||||
if data.perspective.id == user.id {
|
if data.perspective.id == user.id {
|
||||||
return u32::MAX;
|
return u32::MAX;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use crate::util::variables::delta::{JANUARY_URL, MAX_EMBED_COUNT};
|
use crate::util::variables::delta::{JANUARY_URL, MAX_EMBED_COUNT, JANUARY_CONCURRENT_CONNECTIONS};
|
||||||
use crate::{
|
use crate::{
|
||||||
models::{message::AppendMessage, Message},
|
models::{message::AppendMessage, Message},
|
||||||
types::january::Embed,
|
types::january::Embed,
|
||||||
Database,
|
Database,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use async_lock::Semaphore;
|
||||||
|
use async_std::task::spawn;
|
||||||
use deadqueue::limited::Queue;
|
use deadqueue::limited::Queue;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Task information
|
/// Task information
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -36,21 +39,30 @@ pub async fn queue(channel: String, id: String, content: String) {
|
|||||||
|
|
||||||
/// Start a new worker
|
/// Start a new worker
|
||||||
pub async fn worker(db: Database) {
|
pub async fn worker(db: Database) {
|
||||||
|
let semaphore = Arc::new(Semaphore::new(*JANUARY_CONCURRENT_CONNECTIONS));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let task = Q.pop().await;
|
let task = Q.pop().await;
|
||||||
if let Ok(embeds) = Embed::generate(task.content, &JANUARY_URL, *MAX_EMBED_COUNT).await {
|
let db = db.clone();
|
||||||
if let Err(err) = Message::append(
|
let semaphore = semaphore.clone();
|
||||||
&db,
|
|
||||||
task.id,
|
spawn(async move {
|
||||||
task.channel,
|
let embeds = Embed::generate(task.content, &JANUARY_URL, *MAX_EMBED_COUNT, semaphore).await;
|
||||||
AppendMessage {
|
|
||||||
embeds: Some(embeds),
|
if let Ok(embeds) = embeds {
|
||||||
},
|
if let Err(err) = Message::append(
|
||||||
)
|
&db,
|
||||||
.await
|
task.id,
|
||||||
{
|
task.channel,
|
||||||
error!("Encountered an error appending to message: {:?}", err);
|
AppendMessage {
|
||||||
|
embeds: Some(embeds),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!("Encountered an error appending to message: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::bson::doc;
|
use crate::bson::doc;
|
||||||
use crate::util::variables::delta::VAPID_PRIVATE_KEY;
|
use crate::util::variables::delta::VAPID_PRIVATE_KEY;
|
||||||
|
|
||||||
|
use authifier::Database;
|
||||||
use deadqueue::limited::Queue;
|
use deadqueue::limited::Queue;
|
||||||
use rauth::Database;
|
|
||||||
use web_push::{
|
use web_push::{
|
||||||
ContentEncoding, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, WebPushClient,
|
ContentEncoding, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, WebPushClient,
|
||||||
WebPushMessageBuilder,
|
WebPushMessageBuilder,
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
use crate::{models::stats::Stats, Result};
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractStats: Sync + Send {
|
||||||
|
async fn generate_stats(&self) -> Result<Stats>;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::models::message::{AppendMessage, Message, MessageSort, PartialMessage};
|
use crate::models::message::{AppendMessage, Message, MessageQuery, PartialMessage};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -21,27 +21,8 @@ pub trait AbstractMessage: Sync + Send {
|
|||||||
/// Delete messages from a channel by their ids and corresponding channel id
|
/// Delete messages from a channel by their ids and corresponding channel id
|
||||||
async fn delete_messages(&self, channel: &str, ids: Vec<String>) -> Result<()>;
|
async fn delete_messages(&self, channel: &str, ids: Vec<String>) -> Result<()>;
|
||||||
|
|
||||||
/// Fetch multiple messages
|
/// Fetch multiple messages by given query
|
||||||
async fn fetch_messages(
|
async fn fetch_messages(&self, query: MessageQuery) -> Result<Vec<Message>>;
|
||||||
&self,
|
|
||||||
channel: &str,
|
|
||||||
limit: Option<i64>,
|
|
||||||
before: Option<String>,
|
|
||||||
after: Option<String>,
|
|
||||||
sort: Option<MessageSort>,
|
|
||||||
nearby: Option<String>,
|
|
||||||
) -> Result<Vec<Message>>;
|
|
||||||
|
|
||||||
/// Search for messages
|
|
||||||
async fn search_messages(
|
|
||||||
&self,
|
|
||||||
channel: &str,
|
|
||||||
query: &str,
|
|
||||||
limit: Option<i64>,
|
|
||||||
before: Option<String>,
|
|
||||||
after: Option<String>,
|
|
||||||
sort: MessageSort,
|
|
||||||
) -> Result<Vec<Message>>;
|
|
||||||
|
|
||||||
/// Add a new reaction to a message
|
/// Add a new reaction to a message
|
||||||
async fn add_reaction(&self, id: &str, emoji: &str, user: &str) -> Result<()>;
|
async fn add_reaction(&self, id: &str, emoji: &str, user: &str) -> Result<()>;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod admin {
|
mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod media {
|
mod media {
|
||||||
@@ -26,7 +27,13 @@ mod users {
|
|||||||
pub mod user_settings;
|
pub mod user_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod safety {
|
||||||
|
pub mod report;
|
||||||
|
pub mod snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
pub use admin::migrations::AbstractMigrations;
|
pub use admin::migrations::AbstractMigrations;
|
||||||
|
pub use admin::stats::AbstractStats;
|
||||||
|
|
||||||
pub use media::attachment::AbstractAttachment;
|
pub use media::attachment::AbstractAttachment;
|
||||||
pub use media::emoji::AbstractEmoji;
|
pub use media::emoji::AbstractEmoji;
|
||||||
@@ -44,10 +51,14 @@ pub use users::bot::AbstractBot;
|
|||||||
pub use users::user::AbstractUser;
|
pub use users::user::AbstractUser;
|
||||||
pub use users::user_settings::AbstractUserSettings;
|
pub use users::user_settings::AbstractUserSettings;
|
||||||
|
|
||||||
|
pub use safety::report::AbstractReport;
|
||||||
|
pub use safety::snapshot::AbstractSnapshot;
|
||||||
|
|
||||||
pub trait AbstractDatabase:
|
pub trait AbstractDatabase:
|
||||||
Sync
|
Sync
|
||||||
+ Send
|
+ Send
|
||||||
+ AbstractMigrations
|
+ AbstractMigrations
|
||||||
|
+ AbstractStats
|
||||||
+ AbstractAttachment
|
+ AbstractAttachment
|
||||||
+ AbstractEmoji
|
+ AbstractEmoji
|
||||||
+ AbstractChannel
|
+ AbstractChannel
|
||||||
@@ -60,5 +71,7 @@ pub trait AbstractDatabase:
|
|||||||
+ AbstractBot
|
+ AbstractBot
|
||||||
+ AbstractUser
|
+ AbstractUser
|
||||||
+ AbstractUserSettings
|
+ AbstractUserSettings
|
||||||
|
+ AbstractReport
|
||||||
|
+ AbstractSnapshot
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
use crate::models::report::PartialReport;
|
||||||
|
use crate::models::Report;
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractReport: Sync + Send {
|
||||||
|
/// Insert a new report into the database
|
||||||
|
async fn insert_report(&self, report: &Report) -> Result<()>;
|
||||||
|
|
||||||
|
/// Update a given report with new information
|
||||||
|
async fn update_report(&self, id: &str, message: &PartialReport) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch report
|
||||||
|
async fn fetch_report(&self, report_id: &str) -> Result<Report>;
|
||||||
|
|
||||||
|
/// Fetch reports
|
||||||
|
async fn fetch_reports(&self) -> Result<Vec<Report>>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
use crate::models::Snapshot;
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractSnapshot: Sync + Send {
|
||||||
|
/// Insert a new snapshot into the database
|
||||||
|
async fn insert_snapshot(&self, snapshot: &Snapshot) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch a snapshot by a report's id
|
||||||
|
async fn fetch_snapshot(&self, report_id: &str) -> Result<Snapshot>;
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
|
use async_lock::Semaphore;
|
||||||
|
use async_std::task::spawn;
|
||||||
|
use futures::future::join_all;
|
||||||
use linkify::{LinkFinder, LinkKind};
|
use linkify::{LinkFinder, LinkKind};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use crate::{models::attachment::File, Error, Result};
|
use crate::{models::attachment::File, Error, Result};
|
||||||
|
|
||||||
@@ -95,6 +98,8 @@ pub enum Special {
|
|||||||
content_type: BandcampType,
|
content_type: BandcampType,
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
|
/// Streamable Video
|
||||||
|
Streamable { id: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Website metadata
|
/// Website metadata
|
||||||
@@ -172,7 +177,12 @@ pub enum Embed {
|
|||||||
|
|
||||||
impl Embed {
|
impl Embed {
|
||||||
/// Generate embeds from given content
|
/// Generate embeds from given content
|
||||||
pub async fn generate(content: String, host: &str, max_embeds: usize) -> Result<Vec<Embed>> {
|
pub async fn generate(
|
||||||
|
content: String,
|
||||||
|
host: &str,
|
||||||
|
max_embeds: usize,
|
||||||
|
semaphore: Arc<Semaphore>,
|
||||||
|
) -> Result<Vec<Embed>> {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE_CODE: Regex = Regex::new("```(?:.|\n)+?```|`(?:.|\n)+?`").unwrap();
|
static ref RE_CODE: Regex = Regex::new("```(?:.|\n)+?```|`(?:.|\n)+?`").unwrap();
|
||||||
static ref RE_IGNORED: Regex = Regex::new("(<http.+>)").unwrap();
|
static ref RE_IGNORED: Regex = Regex::new("(<http.+>)").unwrap();
|
||||||
@@ -223,26 +233,37 @@ impl Embed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ! FIXME: batch request to january?
|
// ! FIXME: batch request to january?
|
||||||
let mut embeds: Vec<Embed> = Vec::new();
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let mut tasks = Vec::new();
|
||||||
|
let url = format!("{host}/embed");
|
||||||
|
|
||||||
for link in links {
|
for link in links {
|
||||||
let result = client
|
let client = client.clone();
|
||||||
.get(&format!("{}/embed", host))
|
let url = url.clone();
|
||||||
.query(&[("url", link)])
|
let semaphore = semaphore.clone();
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if result.is_err() {
|
tasks.push(spawn(async move {
|
||||||
continue;
|
let guard = semaphore.acquire().await;
|
||||||
}
|
|
||||||
|
|
||||||
let response = result.unwrap();
|
let response = client.get(url).query(&[("url", link)]).send().await.ok()?;
|
||||||
if response.status().is_success() {
|
|
||||||
let res: Embed = response.json().await.map_err(|_| Error::InvalidOperation)?;
|
drop(guard);
|
||||||
embeds.push(res);
|
|
||||||
}
|
if response.status().is_success() {
|
||||||
|
response.json::<Embed>().await.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let embeds = join_all(tasks)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<Embed>>();
|
||||||
|
|
||||||
// Prevent database update when no embeds are found.
|
// Prevent database update when no embeds are found.
|
||||||
if !embeds.is_empty() {
|
if !embeds.is_empty() {
|
||||||
Ok(embeds)
|
Ok(embeds)
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
use authifier::config::{ResolveIp, Shield};
|
||||||
|
|
||||||
use super::variables::delta::{
|
use super::variables::delta::{
|
||||||
APP_URL, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD, SMTP_USERNAME,
|
APP_URL, AUTHIFIER_SHIELD_KEY, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD,
|
||||||
USE_EMAIL, USE_HCAPTCHA,
|
SMTP_USERNAME, USE_EMAIL, USE_HCAPTCHA,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::rauth::config::{
|
use crate::authifier::config::{
|
||||||
Captcha, Config, EmailVerificationConfig, SMTPSettings, Template, Templates,
|
Captcha, Config, EmailVerificationConfig, SMTPSettings, Template, Templates,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,5 +61,19 @@ pub fn config() -> Config {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(api_key) = &*AUTHIFIER_SHIELD_KEY {
|
||||||
|
config.shield = Shield::Enabled {
|
||||||
|
api_key: api_key.to_string(),
|
||||||
|
strict: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("TRUST_CLOUDFLARE")
|
||||||
|
.map(|x| x == "1")
|
||||||
|
.unwrap_or_default()
|
||||||
|
{
|
||||||
|
config.resolve_ip = ResolveIp::Cloudflare;
|
||||||
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
pub mod authifier;
|
||||||
pub mod log;
|
pub mod log;
|
||||||
pub mod manipulation;
|
pub mod manipulation;
|
||||||
pub mod pfp;
|
pub mod pfp;
|
||||||
pub mod rauth;
|
|
||||||
pub mod r#ref;
|
pub mod r#ref;
|
||||||
pub mod regex;
|
pub mod regex;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::models::{Bot, Channel, Emoji, Invite, Member, Message, Server, ServerBan, User};
|
use crate::models::{
|
||||||
|
Bot, Channel, Emoji, Invite, Member, Message, Report, Server, ServerBan, User,
|
||||||
|
};
|
||||||
use crate::presence::presence_is_online;
|
use crate::presence::presence_is_online;
|
||||||
use crate::{Database, Error, Result};
|
use crate::{Database, Error, Result};
|
||||||
|
|
||||||
@@ -78,6 +80,11 @@ impl Ref {
|
|||||||
pub async fn as_emoji(&self, db: &Database) -> Result<Emoji> {
|
pub async fn as_emoji(&self, db: &Database) -> Result<Emoji> {
|
||||||
db.fetch_emoji(&self.id).await
|
db.fetch_emoji(&self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch report from Ref
|
||||||
|
pub async fn as_report(&self, db: &Database) -> Result<Report> {
|
||||||
|
db.fetch_report(&self.id).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> FromParam<'r> for Ref {
|
impl<'r> FromParam<'r> for Ref {
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ pub enum Error {
|
|||||||
/// This error was not labeled :(
|
/// This error was not labeled :(
|
||||||
LabelMe,
|
LabelMe,
|
||||||
|
|
||||||
// ? Onboarding related errors.
|
// ? Onboarding related errors
|
||||||
AlreadyOnboarded,
|
AlreadyOnboarded,
|
||||||
|
|
||||||
// ? User related errors.
|
// ? User related errors
|
||||||
UsernameTaken,
|
UsernameTaken,
|
||||||
InvalidUsername,
|
InvalidUsername,
|
||||||
UnknownUser,
|
UnknownUser,
|
||||||
@@ -33,7 +33,7 @@ pub enum Error {
|
|||||||
BlockedByOther,
|
BlockedByOther,
|
||||||
NotFriends,
|
NotFriends,
|
||||||
|
|
||||||
// ? Channel related errors.
|
// ? Channel related errors
|
||||||
UnknownChannel,
|
UnknownChannel,
|
||||||
UnknownAttachment,
|
UnknownAttachment,
|
||||||
UnknownMessage,
|
UnknownMessage,
|
||||||
@@ -50,7 +50,7 @@ pub enum Error {
|
|||||||
AlreadyInGroup,
|
AlreadyInGroup,
|
||||||
NotInGroup,
|
NotInGroup,
|
||||||
|
|
||||||
// ? Server related errors.
|
// ? Server related errors
|
||||||
UnknownServer,
|
UnknownServer,
|
||||||
InvalidRole,
|
InvalidRole,
|
||||||
Banned,
|
Banned,
|
||||||
@@ -59,12 +59,15 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
TooManyEmoji,
|
TooManyEmoji,
|
||||||
|
|
||||||
// ? Bot related errors.
|
// ? Bot related errors
|
||||||
ReachedMaximumBots,
|
ReachedMaximumBots,
|
||||||
IsBot,
|
IsBot,
|
||||||
BotIsPrivate,
|
BotIsPrivate,
|
||||||
|
|
||||||
// ? Permission errors.
|
// ? User safety related errors
|
||||||
|
CannotReportYourself,
|
||||||
|
|
||||||
|
// ? Permission errors
|
||||||
MissingPermission {
|
MissingPermission {
|
||||||
permission: Permission,
|
permission: Permission,
|
||||||
},
|
},
|
||||||
@@ -72,10 +75,11 @@ pub enum Error {
|
|||||||
permission: UserPermission,
|
permission: UserPermission,
|
||||||
},
|
},
|
||||||
NotElevated,
|
NotElevated,
|
||||||
|
NotPrivileged,
|
||||||
CannotGiveMissingPermissions,
|
CannotGiveMissingPermissions,
|
||||||
NotOwner,
|
NotOwner,
|
||||||
|
|
||||||
// ? General errors.
|
// ? General errors
|
||||||
DatabaseError {
|
DatabaseError {
|
||||||
operation: &'static str,
|
operation: &'static str,
|
||||||
with: &'static str,
|
with: &'static str,
|
||||||
@@ -83,6 +87,7 @@ pub enum Error {
|
|||||||
InternalError,
|
InternalError,
|
||||||
InvalidOperation,
|
InvalidOperation,
|
||||||
InvalidCredentials,
|
InvalidCredentials,
|
||||||
|
InvalidProperty,
|
||||||
InvalidSession,
|
InvalidSession,
|
||||||
DuplicateNonce,
|
DuplicateNonce,
|
||||||
VosoUnavailable,
|
VosoUnavailable,
|
||||||
@@ -165,9 +170,12 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
Error::IsBot => Status::BadRequest,
|
Error::IsBot => Status::BadRequest,
|
||||||
Error::BotIsPrivate => Status::Forbidden,
|
Error::BotIsPrivate => Status::Forbidden,
|
||||||
|
|
||||||
|
Error::CannotReportYourself => Status::BadRequest,
|
||||||
|
|
||||||
Error::MissingPermission { .. } => Status::Forbidden,
|
Error::MissingPermission { .. } => Status::Forbidden,
|
||||||
Error::MissingUserPermission { .. } => Status::Forbidden,
|
Error::MissingUserPermission { .. } => Status::Forbidden,
|
||||||
Error::NotElevated => Status::Forbidden,
|
Error::NotElevated => Status::Forbidden,
|
||||||
|
Error::NotPrivileged => Status::Forbidden,
|
||||||
Error::CannotGiveMissingPermissions => Status::Forbidden,
|
Error::CannotGiveMissingPermissions => Status::Forbidden,
|
||||||
Error::NotOwner => Status::Forbidden,
|
Error::NotOwner => Status::Forbidden,
|
||||||
|
|
||||||
@@ -175,6 +183,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
Error::InternalError => Status::InternalServerError,
|
Error::InternalError => Status::InternalServerError,
|
||||||
Error::InvalidOperation => Status::BadRequest,
|
Error::InvalidOperation => Status::BadRequest,
|
||||||
Error::InvalidCredentials => Status::Unauthorized,
|
Error::InvalidCredentials => Status::Unauthorized,
|
||||||
|
Error::InvalidProperty => Status::BadRequest,
|
||||||
Error::InvalidSession => Status::Unauthorized,
|
Error::InvalidSession => Status::Unauthorized,
|
||||||
Error::DuplicateNonce => Status::Conflict,
|
Error::DuplicateNonce => Status::Conflict,
|
||||||
Error::VosoUnavailable => Status::BadRequest,
|
Error::VosoUnavailable => Status::BadRequest,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ lazy_static! {
|
|||||||
env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
|
env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
|
||||||
pub static ref JANUARY_URL: String =
|
pub static ref JANUARY_URL: String =
|
||||||
env::var("JANUARY_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
|
env::var("JANUARY_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
|
||||||
|
pub static ref JANUARY_CONCURRENT_CONNECTIONS: usize =
|
||||||
|
env::var("JANUARY_CONCURRENT_CONNECTIONS").map_or(50, |v| v.parse().unwrap());
|
||||||
pub static ref VOSO_URL: String =
|
pub static ref VOSO_URL: String =
|
||||||
env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
|
env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
|
||||||
pub static ref VOSO_WS_HOST: String =
|
pub static ref VOSO_WS_HOST: String =
|
||||||
@@ -28,6 +30,8 @@ lazy_static! {
|
|||||||
env::var("REVOLT_VAPID_PRIVATE_KEY").expect("Missing REVOLT_VAPID_PRIVATE_KEY environment variable.");
|
env::var("REVOLT_VAPID_PRIVATE_KEY").expect("Missing REVOLT_VAPID_PRIVATE_KEY environment variable.");
|
||||||
pub static ref VAPID_PUBLIC_KEY: String =
|
pub static ref VAPID_PUBLIC_KEY: String =
|
||||||
env::var("REVOLT_VAPID_PUBLIC_KEY").expect("Missing REVOLT_VAPID_PUBLIC_KEY environment variable.");
|
env::var("REVOLT_VAPID_PUBLIC_KEY").expect("Missing REVOLT_VAPID_PUBLIC_KEY environment variable.");
|
||||||
|
pub static ref AUTHIFIER_SHIELD_KEY: Option<String> =
|
||||||
|
env::var("REVOLT_AUTHIFIER_SHIELD_KEY").ok();
|
||||||
|
|
||||||
// Application Flags
|
// Application Flags
|
||||||
pub static ref INVITE_ONLY: bool = env::var("REVOLT_INVITE_ONLY").map_or(false, |v| v == "1");
|
pub static ref INVITE_ONLY: bool = env::var("REVOLT_INVITE_ONLY").map_or(false, |v| v == "1");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use std::hash::Hasher;
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use crate::rauth::models::Session;
|
use crate::authifier::models::Session;
|
||||||
use rocket::fairing::{Fairing, Info, Kind};
|
use rocket::fairing::{Fairing, Info, Kind};
|
||||||
use rocket::http::uri::Origin;
|
use rocket::http::uri::Origin;
|
||||||
use rocket::http::{Method, Status};
|
use rocket::http::{Method, Status};
|
||||||
@@ -129,6 +129,8 @@ fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
("swagger", _) => ("swagger", None),
|
("swagger", _) => ("swagger", None),
|
||||||
|
("safety", Some("report")) => ("safety_report", Some("report")),
|
||||||
|
("safety", _) => ("safety", None),
|
||||||
_ => ("any", None),
|
_ => ("any", None),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -148,6 +150,8 @@ fn resolve_bucket_limit(bucket: &str) -> u8 {
|
|||||||
"auth_delete" => 255,
|
"auth_delete" => 255,
|
||||||
"default_avatar" => 255,
|
"default_avatar" => 255,
|
||||||
"swagger" => 100,
|
"swagger" => 100,
|
||||||
|
"safety" => 15,
|
||||||
|
"safety_report" => 3,
|
||||||
_ => 20,
|
_ => 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user