Compare commits

...
29 Commits
Author SHA1 Message Date
Paul Makles c09244039e chore: bump and release version 2023-03-01 19:14:31 +00:00
Paul Makles bf9108408e feat(safety): fetch additional context for snapshot 2023-03-01 15:09:17 +00:00
Paul Makles 304336d905 feat(safety): fetch reports and fetch snapshot API 2023-03-01 11:22:22 +00:00
Paul Makles 42b4906594 chore: add database migrations for safety 2023-03-01 11:21:58 +00:00
Paul Makles 3038fb230f feat(safety): save surrounding context around messages (closes #226) 2023-02-23 18:12:45 +01:00
Paul Makles 73af105feb feat(safety): add global event for report creation (closes #225) 2023-02-23 17:58:05 +01:00
Paul Makles 2de37b23c1 merge: branch local cleanup branch 2023-02-22 12:45:39 +01:00
Paul Makles 0c75d8c8b3 refactor: remove old .github directories [skip ci] 2023-02-22 12:44:46 +01:00
Paul Makles be793b1570 chore(ci): update commit author [skip ci] 2023-02-22 12:44:24 +01:00
Paul Makles 5a3a97df34 merge: pull request #223 from revoltchat/insert/feat/user-safety-api 2023-02-22 08:52:37 +00:00
Paul Makles 0c072b01d7 fix(safety): prevent reporting own content or self 2023-02-21 12:42:08 +01:00
Paul Makles 056c0380b2 feat(safety): implement report content API 2023-02-21 12:38:35 +01:00
Paul Makles 49598daf70 fix: port user safety API to new codebase 2023-02-17 09:45:34 +00:00
Paul Makles 0445181d5b merge: branch 'master' into insert/feat/user-safety-api 2023-02-17 09:43:12 +00:00
Paul Makles e780178cc0 fix(ci): move token definition to correct stage 2023-01-29 16:46:19 +00:00
Paul Makles c4b9884825 fix(ci): use correct token 2023-01-29 16:36:44 +00:00
Paul Makles fdab997d17 chore(ci): switch testing to stable Rust 2023-01-29 14:23:56 +00:00
Paul Makles 8e0addc3e0 fix: fallback properly if no VERGEN strings can generate 2023-01-29 14:22:57 +00:00
Paul Makles 0da4baf777 chore: bump version and clean up repo 2023-01-29 14:16:18 +00:00
Paul Makles c03fcda85b refactor: switch from "rauth" to Authifier crate 2023-01-29 14:14:35 +00:00
Paul Makles 0afbcc065f refactor: clippy warning for empty list 2023-01-27 16:18:00 +00:00
Paul Makles a406e5504d chore: add support for Streamable in media struct 2023-01-27 16:17:47 +00:00
Zomatree e8c233f60b feat: concurrently query January for embeds (#221) 2023-01-27 16:06:27 +00:00
insertish 72595f30d4 ci: synced local '.github/workflows/triage_pr.yml' with remote 'workflows/triage_pr.yml'
[skip ci]
2023-01-24 19:43:40 +00:00
insertish 39e345af74 ci: synced local '.github/workflows/triage_issue.yml' with remote 'workflows/triage_issue.yml'
[skip ci]
2023-01-24 19:43:40 +00:00
Paul Makles e0b918771d chore: ensure restrict_reactions is used in conjunction with reactions list 2023-01-20 18:06:44 +00:00
Paul Makles 7a6bd70dcd feat: add route to fetch user flags
chore: bump rauth to change disabled account behaviour
2023-01-20 17:47:26 +00:00
Paul Makles c0ef3d295a fix: fallback if vergen cannot generate data 2023-01-20 17:14:25 +00:00
Paul Makles d74104fae4 feat: initial ideas for content reporting 2023-01-18 12:35:49 +00:00
61 changed files with 1050 additions and 657 deletions
-2
View File
@@ -1,2 +0,0 @@
ko_fi: insertish
custom: https://insrt.uk/donate
+5 -4
View File
@@ -15,10 +15,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install latest nightly
- name: Install latest stable
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
toolchain: stable
override: true
components: rustfmt, clippy
@@ -59,6 +59,7 @@ jobs:
with:
repository: revoltchat/api
path: api
token: ${{ secrets.PAT }}
- name: Download OpenAPI specification
if: github.event_name != 'pull_request'
@@ -70,6 +71,6 @@ jobs:
with:
cwd: "api"
add: "*.json"
author_name: Revolt CI
author_email: revolt-ci@users.noreply.github.com
message: "chore: generate OpenAPI specification"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+16 -11
View File
@@ -15,22 +15,27 @@ jobs:
gh api graphql -f query='
query {
organization(login: "revoltchat"){
projectNext(number: 3) {
projectV2(number: 3) {
id
fields(first:20) {
nodes {
id
name
settings
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}' > 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
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.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.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
- name: Add issue to project
env:
@@ -39,11 +44,11 @@ jobs:
run: |
item_id="$( gh api graphql -f query='
mutation($project:ID!, $issue:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
projectNextItem {
addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
item {
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
+22 -15
View File
@@ -15,22 +15,27 @@ jobs:
gh api graphql -f query='
query {
organization(login: "revoltchat"){
projectNext(number: 3) {
projectV2(number: 3) {
id
fields(first:20) {
nodes {
id
name
settings
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}' > 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
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.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.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
- name: Add PR to project
env:
@@ -39,13 +44,13 @@ jobs:
run: |
item_id="$( gh api graphql -f query='
mutation($project:ID!, $pr:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $pr}) {
projectNextItem {
addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
item {
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
- name: Set fields
@@ -59,14 +64,16 @@ jobs:
$status_field: ID!
$status_value: String!
) {
set_status: updateProjectNextItemField(input: {
set_status: updateProjectV2ItemFieldValue(input: {
projectId: $project
itemId: $item
fieldId: $status_field
value: $status_value
value: {
singleSelectOptionId: $status_value
}
}) {
projectNextItem {
projectV2Item {
id
}
}
}
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent
Generated
+104 -100
View File
@@ -106,7 +106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
dependencies = [
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -172,11 +172,12 @@ dependencies = [
[[package]]
name = "async-lock"
version = "2.5.0"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
dependencies = [
"event-listener",
"futures-lite",
]
[[package]]
@@ -204,7 +205,7 @@ checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -269,7 +270,7 @@ checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -286,7 +287,7 @@ checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -329,6 +330,40 @@ dependencies = [
"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]]
name = "autocfg"
version = "0.1.8"
@@ -681,7 +716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
dependencies = [
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -714,7 +749,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.18",
"strsim",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -725,7 +760,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -774,7 +809,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -807,7 +842,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -881,7 +916,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -901,7 +936,7 @@ checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -1085,7 +1120,7 @@ checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -1176,7 +1211,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -2057,7 +2092,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -2110,7 +2145,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -2215,7 +2250,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -2253,7 +2288,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -2284,7 +2319,7 @@ checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -2371,7 +2406,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
"version_check",
]
@@ -2388,9 +2423,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.39"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [
"unicode-ident",
]
@@ -2403,7 +2438,7 @@ checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
"version_check",
"yansi",
]
@@ -2578,39 +2613,6 @@ dependencies = [
"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]]
name = "rdrand"
version = "0.4.0"
@@ -2704,7 +2706,7 @@ checksum = "a043824e29c94169374ac5183ac0ed43f5724dc4556b19568007486bd840fa1f"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -2790,7 +2792,7 @@ dependencies = [
[[package]]
name = "revolt-bonfire"
version = "0.5.6"
version = "0.5.10"
dependencies = [
"async-std",
"async-tungstenite",
@@ -2806,7 +2808,7 @@ dependencies = [
[[package]]
name = "revolt-delta"
version = "0.5.6"
version = "0.5.10"
dependencies = [
"async-channel",
"async-std",
@@ -2833,8 +2835,8 @@ dependencies = [
"revolt-quark",
"revolt_rocket_okapi",
"rocket",
"rocket_authifier",
"rocket_empty",
"rocket_rauth",
"schemars",
"serde",
"serde_json",
@@ -2846,11 +2848,13 @@ dependencies = [
[[package]]
name = "revolt-quark"
version = "0.5.6"
version = "0.5.10"
dependencies = [
"async-lock",
"async-recursion",
"async-std",
"async-trait",
"authifier",
"base64 0.13.0",
"bincode",
"bitfield",
@@ -2872,7 +2876,6 @@ dependencies = [
"once_cell",
"optional_struct",
"pretty_env_logger",
"rauth",
"redis-kiss",
"regex",
"reqwest",
@@ -2929,7 +2932,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.18",
"rocket_http",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -3008,6 +3011,22 @@ dependencies = [
"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]]
name = "rocket_codegen"
version = "0.5.0-rc.2"
@@ -3020,7 +3039,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.18",
"rocket_http",
"syn 1.0.96",
"syn 1.0.107",
"unicode-xid 0.2.3",
]
@@ -3078,21 +3097,6 @@ dependencies = [
"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]]
name = "rust-argon2"
version = "1.0.0"
@@ -3210,7 +3214,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.18",
"serde_derive_internals",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -3361,9 +3365,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.137"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
@@ -3379,13 +3383,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.137"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -3396,14 +3400,14 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
name = "serde_json"
version = "1.0.81"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"indexmap",
"itoa 1.0.2",
@@ -3442,7 +3446,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -3620,9 +3624,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.96"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote 1.0.18",
@@ -3698,7 +3702,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -3807,7 +3811,7 @@ checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -3943,7 +3947,7 @@ checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -4073,7 +4077,7 @@ checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -4284,7 +4288,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.18",
"regex",
"syn 1.0.96",
"syn 1.0.107",
"validator_types",
]
@@ -4295,7 +4299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded9d97e1d42327632f5f3bae6403c04886e2de3036261ef42deebd931a6a291"
dependencies = [
"proc-macro2",
"syn 1.0.96",
"syn 1.0.107",
]
[[package]]
@@ -4393,7 +4397,7 @@ dependencies = [
"log",
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
"wasm-bindgen-shared",
]
@@ -4427,7 +4431,7 @@ checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
dependencies = [
"proc-macro2",
"quote 1.0.18",
"syn 1.0.96",
"syn 1.0.107",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
-7
View File
@@ -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 .
-2
View File
@@ -1,2 +0,0 @@
ko_fi: insertish
custom: https://insrt.uk/donate
-98
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-bonfire"
version = "0.5.6"
version = "0.5.10"
license = "AGPL-3.0-or-later"
edition = "2021"
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-delta"
version = "0.5.6"
version = "0.5.10"
license = "AGPL-3.0-or-later"
authors = ["Paul Makles <paulmakles@gmail.com>"]
edition = "2018"
@@ -52,7 +52,7 @@ mobc-redis = { version = "0.7.0", default-features = false, features = ["async-s
# web
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
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
schemars = "0.8.8"
+1 -1
View File
@@ -12,5 +12,5 @@ fn main() {
}
}
vergen(Config::default()).unwrap();
vergen(Config::default()).ok();
}
+10 -10
View File
@@ -11,8 +11,8 @@ pub mod routes;
pub mod util;
use async_std::channel::unbounded;
use revolt_quark::authifier::{Authifier, AuthifierEvent};
use revolt_quark::events::client::EventV1;
use revolt_quark::rauth::{RAuth, RAuthEvent};
use revolt_quark::DatabaseInfo;
#[launch]
@@ -27,25 +27,25 @@ async fn rocket() -> _ {
let db = DatabaseInfo::Auto.connect().await.unwrap();
db.migrate_database().await.unwrap();
// Setup rAuth event channel
// Setup Authifier event channel
let (sender, receiver) = unbounded();
// Setup rAuth
let rauth = RAuth {
// Setup Authifier
let authifier = Authifier {
database: db.clone().into(),
config: revolt_quark::util::rauth::config(),
config: revolt_quark::util::authifier::config(),
event_channel: Some(sender),
};
// Launch a listener for rAuth events
// Launch a listener for Authifier events
async_std::task::spawn(async move {
while let Ok(event) = receiver.recv().await {
match &event {
RAuthEvent::CreateSession { .. } | RAuthEvent::CreateAccount { .. } => {
AuthifierEvent::CreateSession { .. } | AuthifierEvent::CreateAccount { .. } => {
EventV1::Auth(event).global().await
}
RAuthEvent::DeleteSession { user_id, .. }
| RAuthEvent::DeleteAllSessions { user_id, .. } => {
AuthifierEvent::DeleteSession { user_id, .. }
| AuthifierEvent::DeleteAllSessions { user_id, .. } => {
let id = user_id.to_string();
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::ratelimiter::routes())
.mount("/swagger/", revolt_quark::web::swagger::routes())
.manage(rauth)
.manage(authifier)
.manage(db)
.manage(cors.clone())
.attach(revolt_quark::web::ratelimiter::RatelimitFairing)
@@ -65,16 +65,20 @@ pub async fn message_send(
data.validate()
.map_err(|error| Error::FailedValidation { error })?;
// Validate Message is within reasonable length limits
Message::validate_sum(&data.content, &data.embeds)?;
// Ensure the request is unique
idempotency.consume_nonce(data.nonce).await?;
// 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?;
// Check the message is not 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.embeds.as_ref().map_or(true, |v| v.is_empty()))
@@ -82,6 +86,22 @@ pub async fn message_send(
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 mut message = Message {
id: message_id.clone(),
+11 -3
View File
@@ -10,6 +10,7 @@ mod invites;
mod onboard;
mod push;
mod root;
mod safety;
mod servers;
mod sync;
mod users;
@@ -27,9 +28,10 @@ pub fn mount(mut rocket: Rocket<Build>) -> Rocket<Build> {
"/servers" => servers::routes(),
"/invites" => invites::routes(),
"/custom" => customisation::routes(),
"/auth/account" => rocket_rauth::routes::account::routes(),
"/auth/session" => rocket_rauth::routes::session::routes(),
"/auth/mfa" => rocket_rauth::routes::mfa::routes(),
"/safety" => safety::routes(),
"/auth/account" => rocket_authifier::routes::account::routes(),
"/auth/session" => rocket_authifier::routes::session::routes(),
"/auth/mfa" => rocket_authifier::routes::mfa::routes(),
"/onboard" => onboard::routes(),
"/push" => push::routes(),
"/sync" => sync::routes(),
@@ -105,6 +107,12 @@ fn custom_openapi_spec() -> OpenApi {
"Emojis"
]
},
{
"name": "Platform Moderation",
"tags": [
"User Safety"
]
},
{
"name": "Authentication",
"tags": [
+3 -1
View File
@@ -1,5 +1,7 @@
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 serde::{Deserialize, Serialize};
+1 -1
View File
@@ -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 serde::Serialize;
+4 -4
View File
@@ -1,7 +1,7 @@
use revolt_quark::{
rauth::{
authifier::{
models::{Session, WebPushSubscription},
RAuth,
Authifier,
},
EmptyResponse, Error, Result,
};
@@ -16,13 +16,13 @@ use rocket::{serde::json::Json, State};
#[openapi(tag = "Web Push")]
#[post("/subscribe", data = "<data>")]
pub async fn req(
rauth: &State<RAuth>,
authifier: &State<Authifier>,
mut session: Session,
data: Json<WebPushSubscription>,
) -> Result<EmptyResponse> {
session.subscription = Some(data.into_inner());
session
.save(rauth)
.save(authifier)
.await
.map(|_| EmptyResponse)
.map_err(|_| Error::DatabaseError {
+3 -3
View File
@@ -1,5 +1,5 @@
use revolt_quark::{
rauth::{models::Session, RAuth},
authifier::{models::Session, Authifier},
EmptyResponse, Error, Result,
};
@@ -10,10 +10,10 @@ use rocket::State;
/// Remove the Web Push subscription associated with the current session.
#[openapi(tag = "Web Push")]
#[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
.save(rauth)
.save(authifier)
.await
.map(|_| EmptyResponse)
.map_err(|_| Error::DatabaseError {
+15 -5
View File
@@ -119,11 +119,21 @@ pub async fn root() -> Result<Json<RevoltConfig>> {
app: APP_URL.to_string(),
vapid: VAPID_PUBLIC_KEY.to_string(),
build: BuildInformation {
commit_sha: env!("VERGEN_GIT_SHA").to_string(),
commit_timestamp: env!("VERGEN_GIT_COMMIT_TIMESTAMP").to_string(),
semver: env!("VERGEN_GIT_SEMVER").to_string(),
origin_url: env!("GIT_ORIGIN_URL", "<missing>").to_string(),
timestamp: env!("VERGEN_BUILD_TIMESTAMP").to_string(),
commit_sha: option_env!("VERGEN_GIT_SHA")
.unwrap_or_else(|| "<failed to generate>")
.to_string(),
commit_timestamp: option_env!("VERGEN_GIT_COMMIT_TIMESTAMP")
.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,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,77 @@
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);
}
_ => {
todo!()
}
}
// 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,
}))
}
+17
View File
@@ -0,0 +1,17 @@
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
use rocket::Route;
mod fetch_reports;
mod report_content;
mod fetch_snapshot;
pub fn routes() -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
// Reports
report_content::report_content,
fetch_reports::fetch_reports,
// Snapshots
fetch_snapshot::fetch_snapshot
]
}
@@ -0,0 +1,160 @@
use revolt_quark::events::client::EventV1;
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(
&message.channel,
Some(15),
Some(message.id.to_string()),
None,
None,
None,
)
.await?;
// Collect leading context
let leading_context = db
.fetch_messages(
&message.channel,
Some(15),
None,
Some(message.id.to_string()),
None,
None,
)
.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,
};
db.insert_report(&report).await?;
EventV1::ReportCreate(report).global().await;
Ok(())
}
@@ -1,5 +1,5 @@
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 serde::{Deserialize, Serialize};
use validator::Validate;
@@ -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 }))
}
+2
View File
@@ -9,6 +9,7 @@ mod fetch_dms;
mod fetch_profile;
mod fetch_self;
mod fetch_user;
mod fetch_user_flags;
mod find_mutual;
mod get_default_avatar;
mod open_dm;
@@ -21,6 +22,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
// User Information
fetch_self::req,
fetch_user::req,
fetch_user_flags::fetch_user_flags,
edit_user::req,
change_username::req,
get_default_avatar::req,
-2
View File
@@ -1,2 +0,0 @@
ko_fi: insertish
custom: https://insrt.uk/donate
-33
View File
@@ -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
View File
@@ -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
View File
@@ -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
+7 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "revolt-quark"
version = "0.5.6"
version = "0.5.10"
license = "AGPL-3.0-or-later"
edition = "2021"
@@ -16,9 +16,9 @@ rocket_impl = [
"lru",
"dashmap",
"rauth/database-mongodb",
"rauth/rocket_impl",
"rauth/okapi_impl"
"authifier/database-mongodb",
"authifier/rocket_impl",
"authifier/okapi_impl"
]
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl" ]
@@ -69,6 +69,7 @@ reqwest = "0.11.10"
bitfield = "0.13.2"
once_cell = "1.13.0"
lazy_static = "1.4.0"
async-lock = "2.6.0"
lru = { version = "0.7.6", 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_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "c17e8145baa4790319fdb6a473e465b960f55e7c" }
# rAuth
rauth = { git = "https://github.com/insertish/rauth", tag = "1.0.3", features = [ "async-std-runtime" ] }
# Authifier
authifier = { version = "1.0.7", features = [ "async-std-runtime" ] }
# Sentry
sentry = "0.25.0"
+5 -5
View File
@@ -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 {
match val {
Database::Dummy(_) => rauth::Database::default(),
Database::MongoDb(MongoDb(client)) => {
rauth::Database::MongoDb(rauth::database::MongoDb(client.database("revolt")))
}
Database::Dummy(_) => authifier::Database::default(),
Database::MongoDb(MongoDb(client)) => authifier::Database::MongoDb(
authifier::database::MongoDb(client.database("revolt")),
),
}
}
}
+6 -3
View File
@@ -1,4 +1,4 @@
use rauth::RAuthEvent;
use authifier::AuthifierEvent;
use serde::{Deserialize, Serialize};
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_member::{FieldsMember, MemberCompositeKey, PartialMember};
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;
/// WebSocket Client Errors
@@ -213,6 +213,9 @@ pub enum EventV1 {
/// Delete emoji
EmojiDelete { id: String },
/// New report
ReportCreate(Report),
/// Auth events
Auth(RAuthEvent),
Auth(AuthifierEvent),
}
+5
View File
@@ -28,6 +28,11 @@ pub mod users {
pub mod user_settings;
}
pub mod safety {
pub mod report;
pub mod snapshot;
}
#[derive(Debug, Clone)]
pub struct DummyDb;
@@ -0,0 +1,16 @@
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 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!()
}
}
@@ -57,6 +57,14 @@ pub async fn create_database(db: &MongoDb) {
.await
.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)
.await
.expect("Failed to create bots collection.");
@@ -103,18 +111,18 @@ pub async fn create_database(db: &MongoDb) {
},
"name": "content"
},
{
"key": {
"channel": 1_i32
},
"name": "channel"
},
{
"key": {
"channel": 1_i32,
"_id": 1_i32
},
"name": "channel_id_compound"
},
{
"key": {
"author": 1_i32
},
"name": "author"
}
]
},
@@ -16,7 +16,7 @@ struct MigrationInfo {
revision: i32,
}
pub const LATEST_REVISION: i32 = 18;
pub const LATEST_REVISION: i32 = 21;
pub async fn migrate_database(db: &MongoDb) {
let migrations = db.col::<Document>("migrations");
@@ -215,7 +215,7 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
}
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()
.run_command(
@@ -603,20 +603,20 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
}
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()));
db.run_migration(rauth::Migration::M2022_06_03EnsureUpToSpec)
let db = authifier::Database::MongoDb(authifier::database::MongoDb(db.db()));
db.run_migration(authifier::Migration::M2022_06_03EnsureUpToSpec)
.await
.unwrap();
}
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()));
rauth_db
.run_migration(rauth::Migration::M2022_06_09AddIndexForDeletion)
let authifier_db = authifier::Database::MongoDb(authifier::database::MongoDb(db.db()));
authifier_db
.run_migration(authifier::Migration::M2022_06_09AddIndexForDeletion)
.await
.unwrap();
@@ -661,6 +661,103 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.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`.
// Reminder to update LATEST_REVISION when adding new migrations.
+5
View File
@@ -39,6 +39,11 @@ pub mod users {
pub mod user_settings;
}
pub mod safety {
pub mod report;
pub mod snapshot;
}
#[derive(Debug, Clone)]
pub struct MongoDb(pub mongodb::Client);
@@ -0,0 +1,17 @@
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 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
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
use rauth::models::Session;
use authifier::models::Session;
use revolt_okapi::openapi3::{SecurityScheme, SecuritySchemeData};
use revolt_rocket_okapi::gen::OpenApiGenerator;
use revolt_rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
@@ -12,7 +12,7 @@ use crate::Database;
#[rocket::async_trait]
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> {
let user: &Option<User> = request
@@ -43,7 +43,7 @@ impl<'r> FromRequest<'r> for User {
if let Some(user) = user {
Outcome::Success(user.clone())
} else {
Outcome::Failure((Status::Unauthorized, rauth::Error::InvalidSession))
Outcome::Failure((Status::Unauthorized, authifier::Error::InvalidSession))
}
}
}
+1 -1
View File
@@ -17,8 +17,8 @@ extern crate bitfield;
#[macro_use]
extern crate bson;
pub use authifier;
pub use iso8601_timestamp::Timestamp;
pub use rauth;
pub use redis_kiss;
pub mod events;
@@ -98,6 +98,8 @@ pub struct Interactions {
#[serde(skip_serializing_if = "Option::is_none", default)]
pub reactions: Option<IndexSet<String>>,
/// 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)]
pub restrict_reactions: bool,
}
+8
View File
@@ -27,9 +27,15 @@ mod users {
pub mod user_settings;
}
mod safety {
pub mod report;
pub mod snapshot;
}
pub use admin::*;
pub use channels::*;
pub use media::*;
pub use safety::*;
pub use servers::*;
pub use users::*;
@@ -41,9 +47,11 @@ pub use channel_unread::ChannelUnread;
pub use emoji::Emoji;
pub use message::Message;
pub use migrations::MigrationInfo;
pub use report::Report;
pub use server::Server;
pub use server_ban::ServerBan;
pub use server_member::Member;
pub use simple::SimpleModel;
pub use snapshot::Snapshot;
pub use user::User;
pub use user_settings::UserSettings;
+103
View File
@@ -0,0 +1,103 @@
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, Clone)]
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
#[serde(flatten)]
pub status: ReportStatus,
}
@@ -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>,
}
+26 -14
View File
@@ -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::{
models::{message::AppendMessage, Message},
types::january::Embed,
Database,
};
use async_lock::Semaphore;
use async_std::task::spawn;
use deadqueue::limited::Queue;
use std::sync::Arc;
/// Task information
#[derive(Debug)]
@@ -36,21 +39,30 @@ pub async fn queue(channel: String, id: String, content: String) {
/// Start a new worker
pub async fn worker(db: Database) {
let semaphore = Arc::new(Semaphore::new(*JANUARY_CONCURRENT_CONNECTIONS));
loop {
let task = Q.pop().await;
if let Ok(embeds) = Embed::generate(task.content, &JANUARY_URL, *MAX_EMBED_COUNT).await {
if let Err(err) = Message::append(
&db,
task.id,
task.channel,
AppendMessage {
embeds: Some(embeds),
},
)
.await
{
error!("Encountered an error appending to message: {:?}", err);
let db = db.clone();
let semaphore = semaphore.clone();
spawn(async move {
let embeds = Embed::generate(task.content, &JANUARY_URL, *MAX_EMBED_COUNT, semaphore).await;
if let Ok(embeds) = embeds {
if let Err(err) = Message::append(
&db,
task.id,
task.channel,
AppendMessage {
embeds: Some(embeds),
},
)
.await
{
error!("Encountered an error appending to message: {:?}", err);
}
}
}
});
}
}
+1 -1
View File
@@ -1,8 +1,8 @@
use crate::bson::doc;
use crate::util::variables::delta::VAPID_PRIVATE_KEY;
use authifier::Database;
use deadqueue::limited::Queue;
use rauth::Database;
use web_push::{
ContentEncoding, SubscriptionInfo, SubscriptionKeys, VapidSignatureBuilder, WebPushClient,
WebPushMessageBuilder,
+10
View File
@@ -26,6 +26,11 @@ mod users {
pub mod user_settings;
}
mod safety {
pub mod report;
pub mod snapshot;
}
pub use admin::migrations::AbstractMigrations;
pub use media::attachment::AbstractAttachment;
@@ -44,6 +49,9 @@ pub use users::bot::AbstractBot;
pub use users::user::AbstractUser;
pub use users::user_settings::AbstractUserSettings;
pub use safety::report::AbstractReport;
pub use safety::snapshot::AbstractSnapshot;
pub trait AbstractDatabase:
Sync
+ Send
@@ -60,5 +68,7 @@ pub trait AbstractDatabase:
+ AbstractBot
+ AbstractUser
+ AbstractUserSettings
+ AbstractReport
+ AbstractSnapshot
{
}
+11
View File
@@ -0,0 +1,11 @@
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<()>;
/// 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>;
}
+37 -16
View File
@@ -1,7 +1,10 @@
use async_lock::Semaphore;
use async_std::task::spawn;
use futures::future::join_all;
use linkify::{LinkFinder, LinkKind};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::{collections::HashSet, sync::Arc};
use crate::{models::attachment::File, Error, Result};
@@ -95,6 +98,8 @@ pub enum Special {
content_type: BandcampType,
id: String,
},
/// Streamable Video
Streamable { id: String },
}
/// Website metadata
@@ -172,7 +177,12 @@ pub enum Embed {
impl Embed {
/// 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! {
static ref RE_CODE: Regex = Regex::new("```(?:.|\n)+?```|`(?:.|\n)+?`").unwrap();
static ref RE_IGNORED: Regex = Regex::new("(<http.+>)").unwrap();
@@ -223,26 +233,37 @@ impl Embed {
}
// ! FIXME: batch request to january?
let mut embeds: Vec<Embed> = Vec::new();
let client = reqwest::Client::new();
let mut tasks = Vec::new();
let url = format!("{host}/embed");
for link in links {
let result = client
.get(&format!("{}/embed", host))
.query(&[("url", link)])
.send()
.await;
let client = client.clone();
let url = url.clone();
let semaphore = semaphore.clone();
if result.is_err() {
continue;
}
tasks.push(spawn(async move {
let guard = semaphore.acquire().await;
let response = result.unwrap();
if response.status().is_success() {
let res: Embed = response.json().await.map_err(|_| Error::InvalidOperation)?;
embeds.push(res);
}
let response = client.get(url).query(&[("url", link)]).send().await.ok()?;
drop(guard);
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.
if !embeds.is_empty() {
Ok(embeds)
@@ -1,9 +1,11 @@
use authifier::config::{ResolveIp, Shield};
use super::variables::delta::{
APP_URL, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD, SMTP_USERNAME,
USE_EMAIL, USE_HCAPTCHA,
APP_URL, AUTHIFIER_SHIELD_KEY, HCAPTCHA_KEY, INVITE_ONLY, SMTP_FROM, SMTP_HOST, SMTP_PASSWORD,
SMTP_USERNAME, USE_EMAIL, USE_HCAPTCHA,
};
use crate::rauth::config::{
use crate::authifier::config::{
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
}
+1 -1
View File
@@ -1,7 +1,7 @@
pub mod authifier;
pub mod log;
pub mod manipulation;
pub mod pfp;
pub mod rauth;
pub mod r#ref;
pub mod regex;
pub mod result;
+16 -7
View File
@@ -20,10 +20,10 @@ pub enum Error {
/// This error was not labeled :(
LabelMe,
// ? Onboarding related errors.
// ? Onboarding related errors
AlreadyOnboarded,
// ? User related errors.
// ? User related errors
UsernameTaken,
InvalidUsername,
UnknownUser,
@@ -33,7 +33,7 @@ pub enum Error {
BlockedByOther,
NotFriends,
// ? Channel related errors.
// ? Channel related errors
UnknownChannel,
UnknownAttachment,
UnknownMessage,
@@ -50,7 +50,7 @@ pub enum Error {
AlreadyInGroup,
NotInGroup,
// ? Server related errors.
// ? Server related errors
UnknownServer,
InvalidRole,
Banned,
@@ -59,12 +59,15 @@ pub enum Error {
},
TooManyEmoji,
// ? Bot related errors.
// ? Bot related errors
ReachedMaximumBots,
IsBot,
BotIsPrivate,
// ? Permission errors.
// ? User safety related errors
CannotReportYourself,
// ? Permission errors
MissingPermission {
permission: Permission,
},
@@ -72,10 +75,11 @@ pub enum Error {
permission: UserPermission,
},
NotElevated,
NotPrivileged,
CannotGiveMissingPermissions,
NotOwner,
// ? General errors.
// ? General errors
DatabaseError {
operation: &'static str,
with: &'static str,
@@ -83,6 +87,7 @@ pub enum Error {
InternalError,
InvalidOperation,
InvalidCredentials,
InvalidProperty,
InvalidSession,
DuplicateNonce,
VosoUnavailable,
@@ -165,9 +170,12 @@ impl<'r> Responder<'r, 'static> for Error {
Error::IsBot => Status::BadRequest,
Error::BotIsPrivate => Status::Forbidden,
Error::CannotReportYourself => Status::BadRequest,
Error::MissingPermission { .. } => Status::Forbidden,
Error::MissingUserPermission { .. } => Status::Forbidden,
Error::NotElevated => Status::Forbidden,
Error::NotPrivileged => Status::Forbidden,
Error::CannotGiveMissingPermissions => Status::Forbidden,
Error::NotOwner => Status::Forbidden,
@@ -175,6 +183,7 @@ impl<'r> Responder<'r, 'static> for Error {
Error::InternalError => Status::InternalServerError,
Error::InvalidOperation => Status::BadRequest,
Error::InvalidCredentials => Status::Unauthorized,
Error::InvalidProperty => Status::BadRequest,
Error::InvalidSession => Status::Unauthorized,
Error::DuplicateNonce => Status::Conflict,
Error::VosoUnavailable => Status::BadRequest,
+4
View File
@@ -13,6 +13,8 @@ lazy_static! {
env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
pub static ref JANUARY_URL: 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 =
env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_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.");
pub static ref VAPID_PUBLIC_KEY: String =
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
pub static ref INVITE_ONLY: bool = env::var("REVOLT_INVITE_ONLY").map_or(false, |v| v == "1");
+5 -1
View File
@@ -8,7 +8,7 @@ use std::hash::Hasher;
use std::ops::Add;
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::http::uri::Origin;
use rocket::http::{Method, Status};
@@ -129,6 +129,8 @@ fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r
}
}
("swagger", _) => ("swagger", None),
("safety", Some("report")) => ("safety_report", Some("report")),
("safety", _) => ("safety", None),
_ => ("any", None),
}
} else {
@@ -148,6 +150,8 @@ fn resolve_bucket_limit(bucket: &str) -> u8 {
"auth_delete" => 255,
"default_avatar" => 255,
"swagger" => 100,
"safety" => 15,
"safety_report" => 3,
_ => 20,
}
}