Compare commits
9
Commits
20230315-1
...
20230421-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03a28dbb3e | ||
|
|
32542a822e | ||
|
|
1df90ff53b | ||
|
|
487b979f0d | ||
|
|
b9d813d8f0 | ||
|
|
36dd128459 | ||
|
|
144f0d39c6 | ||
|
|
32a294a64a | ||
|
|
b7b70346b4 |
No files matched your search
@@ -20,47 +20,52 @@ on:
|
|||||||
- "Dockerfile"
|
- "Dockerfile"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
base:
|
base:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Build base image (amd64)
|
name: Build base image
|
||||||
steps:
|
steps:
|
||||||
# Configure build environment
|
# Configure build environment
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
# Authenticate with GHCR
|
# Authenticate with GHCR
|
||||||
- name: Login to Github Container Registry
|
- name: Login to Github Container Registry
|
||||||
uses: docker/login-action@v1
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
# Build all projects and cache
|
- name: Build base image
|
||||||
- name: Build Base Image
|
uses: docker/build-push-action@v4
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ghcr.io/revoltchat/base:latest
|
platforms: linux/amd64,linux/arm64
|
||||||
cache-from: type=gha
|
tags: ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
cache-to: type=gha,mode=max
|
cache-from: type=gha,scope=buildx-base-multi-arch
|
||||||
|
cache-to: type=gha,scope=buildx-base-multi-arch,mode=max
|
||||||
|
|
||||||
publish_amd64:
|
publish:
|
||||||
needs: [base]
|
needs: [base]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
project: [delta, bonfire]
|
project: [delta, bonfire]
|
||||||
name: Build ${{ matrix.project }} image (amd64)
|
name: Build ${{ matrix.project }} image
|
||||||
steps:
|
steps:
|
||||||
# Configure build environment
|
# Configure build environment
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
@@ -68,10 +73,11 @@ jobs:
|
|||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
|
registry: docker.io
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Login to Github Container Registry
|
- name: Login to Github Container Registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -86,11 +92,11 @@ jobs:
|
|||||||
{
|
{
|
||||||
"delta": {
|
"delta": {
|
||||||
"path": "crates/delta",
|
"path": "crates/delta",
|
||||||
"tag": "revoltchat/server"
|
"tag": "${{ github.repository_owner }}/server"
|
||||||
},
|
},
|
||||||
"bonfire": {
|
"bonfire": {
|
||||||
"path": "crates/bonfire",
|
"path": "crates/bonfire",
|
||||||
"tag": "revoltchat/bonfire"
|
"tag": "${{ github.repository_owner }}/bonfire"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export_to: output
|
export_to: output
|
||||||
@@ -98,19 +104,21 @@ jobs:
|
|||||||
# Configure metadata
|
# Configure metadata
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v3
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: ${{ steps.export.outputs.tag }}, ghcr.io/${{ steps.export.outputs.tag }}
|
images: |
|
||||||
|
docker.io/${{ steps.export.outputs.tag }}
|
||||||
|
ghcr.io/${{ steps.export.outputs.tag }}
|
||||||
|
|
||||||
# Build crate image
|
# Build crate image
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64,linux/arm64
|
||||||
file: ${{ steps.export.outputs.path }}/Dockerfile
|
file: ${{ steps.export.outputs.path }}/Dockerfile
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
build-args: |
|
||||||
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
Generated
+4
-3
@@ -2792,7 +2792,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.16"
|
version = "0.5.18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
@@ -2808,7 +2808,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.16"
|
version = "0.5.18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -2847,7 +2847,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.16"
|
version = "0.5.18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -2874,6 +2874,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"optional_struct",
|
"optional_struct",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
"rand 0.8.5",
|
||||||
"redis-kiss",
|
"redis-kiss",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
|||||||
+20
-5
@@ -1,12 +1,27 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM rustlang/rust:nightly-slim AS builder
|
FROM --platform="${BUILDPLATFORM}" rustlang/rust:nightly-slim
|
||||||
USER 0:0
|
USER 0:0
|
||||||
WORKDIR /home/rust/src
|
WORKDIR /home/rust/src
|
||||||
|
|
||||||
# Install build requirements
|
ARG TARGETARCH
|
||||||
RUN apt-get update && apt-get install -y libssl-dev pkg-config
|
|
||||||
|
|
||||||
# Build all crates
|
# Install build requirements
|
||||||
|
RUN dpkg --add-architecture "${TARGETARCH}"
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y \
|
||||||
|
make \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev:"${TARGETARCH}"
|
||||||
|
COPY scripts/build-image-layer.sh /tmp/
|
||||||
|
RUN sh /tmp/build-image-layer.sh tools
|
||||||
|
|
||||||
|
# Build all dependencies
|
||||||
COPY Cargo.toml Cargo.lock ./
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY crates/bonfire/Cargo.toml ./crates/bonfire/
|
||||||
|
COPY crates/delta/Cargo.toml ./crates/delta/
|
||||||
|
COPY crates/quark/Cargo.toml ./crates/quark/
|
||||||
|
RUN sh /tmp/build-image-layer.sh deps
|
||||||
|
|
||||||
|
# Build all apps
|
||||||
COPY crates ./crates
|
COPY crates ./crates
|
||||||
RUN cargo build --locked --release
|
RUN sh /tmp/build-image-layer.sh apps
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.16"
|
version = "0.5.18"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/revoltchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
RUN cargo install --locked --path crates/bonfire
|
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
FROM debian:buster-slim
|
FROM debian:bullseye-slim
|
||||||
RUN apt-get update && apt-get install -y ca-certificates
|
RUN apt-get update && \
|
||||||
COPY --from=builder /usr/local/cargo/bin/revolt-bonfire ./
|
apt-get install -y ca-certificates && \
|
||||||
|
apt-get clean
|
||||||
|
COPY --from=builder /home/rust/src/target/release/revolt-bonfire ./
|
||||||
EXPOSE 9000
|
EXPOSE 9000
|
||||||
CMD ["./revolt-bonfire"]
|
CMD ["./revolt-bonfire"]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.16"
|
version = "0.5.18"
|
||||||
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"
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/revoltchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
RUN cargo install --locked --path crates/delta
|
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
FROM debian:buster-slim
|
FROM debian:bullseye-slim
|
||||||
RUN apt-get update && apt-get install -y ca-certificates
|
RUN apt-get update && \
|
||||||
COPY --from=builder /usr/local/cargo/bin/revolt-delta ./
|
apt-get install -y ca-certificates && \
|
||||||
|
apt-get clean
|
||||||
|
COPY --from=builder /home/rust/src/target/release/revolt-delta ./
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
ENV ROCKET_ADDRESS 0.0.0.0
|
ENV ROCKET_ADDRESS 0.0.0.0
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::{Channel, User},
|
get_relationship,
|
||||||
|
models::{user::RelationshipStatus, Channel, User},
|
||||||
perms, Db, EmptyResponse, Error, Permission, Ref, Result,
|
perms, Db, EmptyResponse, Error, Permission, Ref, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,6 +23,13 @@ pub async fn req(db: &Db, user: User, target: Ref, member: Ref) -> Result<EmptyR
|
|||||||
match &channel {
|
match &channel {
|
||||||
Channel::Group { .. } => {
|
Channel::Group { .. } => {
|
||||||
let member = member.as_user(db).await?;
|
let member = member.as_user(db).await?;
|
||||||
|
if !matches!(
|
||||||
|
get_relationship(&user, &member.id),
|
||||||
|
RelationshipStatus::Friend
|
||||||
|
) {
|
||||||
|
return Err(Error::NotFriends);
|
||||||
|
}
|
||||||
|
|
||||||
channel
|
channel
|
||||||
.add_user_to_group(db, &member.id, &user.id)
|
.add_user_to_group(db, &member.id, &user.id)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use revolt_quark::{
|
|||||||
},
|
},
|
||||||
perms,
|
perms,
|
||||||
web::idempotency::IdempotencyKey,
|
web::idempotency::IdempotencyKey,
|
||||||
Db, Error, Permission, Ref, Result,
|
Db, Error, Permission, Ref, Result, variables::delta::{MAX_ATTACHMENT_COUNT, MAX_REPLY_COUNT},
|
||||||
};
|
};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -139,8 +139,8 @@ pub async fn message_send(
|
|||||||
// 4. Verify replies are valid.
|
// 4. Verify replies are valid.
|
||||||
let mut replies = HashSet::new();
|
let mut replies = HashSet::new();
|
||||||
if let Some(entries) = data.replies {
|
if let Some(entries) = data.replies {
|
||||||
if entries.len() > 5 {
|
if entries.len() > *MAX_REPLY_COUNT {
|
||||||
return Err(Error::TooManyReplies);
|
return Err(Error::TooManyReplies { max: *MAX_REPLY_COUNT });
|
||||||
}
|
}
|
||||||
|
|
||||||
for Reply { id, mention } in entries {
|
for Reply { id, mention } in entries {
|
||||||
@@ -191,8 +191,8 @@ pub async fn message_send(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ! FIXME: move this to app config
|
// ! FIXME: move this to app config
|
||||||
if ids.len() > 5 {
|
if ids.len() > *MAX_ATTACHMENT_COUNT {
|
||||||
return Err(Error::TooManyAttachments);
|
return Err(Error::TooManyAttachments { max: *MAX_ATTACHMENT_COUNT} );
|
||||||
}
|
}
|
||||||
|
|
||||||
for attachment_id in ids {
|
for attachment_id in ids {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use revolt_quark::models::emoji::EmojiParent;
|
use revolt_quark::models::emoji::EmojiParent;
|
||||||
use revolt_quark::models::{Emoji, File, User};
|
use revolt_quark::models::{Emoji, File, User};
|
||||||
|
use revolt_quark::variables::delta::MAX_EMOJI_COUNT;
|
||||||
use revolt_quark::{perms, Db, Error, Permission, Result};
|
use revolt_quark::{perms, Db, Error, Permission, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
@@ -55,8 +56,8 @@ pub async fn create_emoji(
|
|||||||
// Check that there are no more than 100 emoji
|
// Check that there are no more than 100 emoji
|
||||||
// ! FIXME: hardcoded upper limit
|
// ! FIXME: hardcoded upper limit
|
||||||
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
||||||
if emojis.len() > 99 {
|
if emojis.len() > *MAX_EMOJI_COUNT {
|
||||||
return Err(Error::TooManyEmoji);
|
return Err(Error::TooManyEmoji { max: *MAX_EMOJI_COUNT });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EmojiParent::Detached => return Err(Error::InvalidOperation),
|
EmojiParent::Detached => return Err(Error::InvalidOperation),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::{server::PartialServer, Channel, User},
|
models::{server::PartialServer, Channel, User},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms, Db, Error, Permission, Ref, Result, variables::delta::MAX_CHANNEL_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -58,6 +58,10 @@ pub async fn req(
|
|||||||
.throw_permission(db, Permission::ManageChannel)
|
.throw_permission(db, Permission::ManageChannel)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if server.channels.len() > *MAX_CHANNEL_COUNT {
|
||||||
|
return Err(Error::TooManyChannels { max: *MAX_CHANNEL_COUNT })
|
||||||
|
};
|
||||||
|
|
||||||
let id = Ulid::new().to_string();
|
let id = Ulid::new().to_string();
|
||||||
let mut channels = server.channels.clone();
|
let mut channels = server.channels.clone();
|
||||||
channels.push(id.clone());
|
channels.push(id.clone());
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ use rocket::serde::json::Json;
|
|||||||
#[openapi(tag = "Server Members")]
|
#[openapi(tag = "Server Members")]
|
||||||
#[get("/<target>/invites")]
|
#[get("/<target>/invites")]
|
||||||
pub async fn req(db: &Db, user: User, target: Ref) -> Result<Json<Vec<Invite>>> {
|
pub async fn req(db: &Db, user: User, target: Ref) -> Result<Json<Vec<Invite>>> {
|
||||||
if user.bot.is_some() {
|
|
||||||
return Err(Error::IsBot);
|
|
||||||
}
|
|
||||||
|
|
||||||
let server = target.as_server(db).await?;
|
let server = target.as_server(db).await?;
|
||||||
perms(&user)
|
perms(&user)
|
||||||
.server(&server)
|
.server(&server)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::{server::Role, User},
|
models::{server::Role, User},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms, Db, Error, Permission, Ref, Result, variables::delta::MAX_ROLE_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -50,6 +50,10 @@ pub async fn req(
|
|||||||
.throw_permission(db, Permission::ManageRole)
|
.throw_permission(db, Permission::ManageRole)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if server.roles.len() > *MAX_ROLE_COUNT {
|
||||||
|
return Err(Error::TooManyRoles { max: *MAX_ROLE_COUNT })
|
||||||
|
};
|
||||||
|
|
||||||
let member_rank = permissions.get_member_rank();
|
let member_rank = permissions.get_member_rank();
|
||||||
let rank = if let Some(given_rank) = data.rank {
|
let rank = if let Some(given_rank) = data.rank {
|
||||||
if given_rank <= member_rank.unwrap_or(i64::MIN) {
|
if given_rank <= member_rank.unwrap_or(i64::MIN) {
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ pub async fn req(
|
|||||||
user: User,
|
user: User,
|
||||||
info: Json<DataCreateServer>,
|
info: Json<DataCreateServer>,
|
||||||
) -> Result<Json<CreateServerResponse>> {
|
) -> Result<Json<CreateServerResponse>> {
|
||||||
|
if user.bot.is_some() {
|
||||||
|
return Err(Error::IsBot);
|
||||||
|
}
|
||||||
|
|
||||||
let info = info.into_inner();
|
let info = info.into_inner();
|
||||||
info.validate()
|
info.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.16"
|
version = "0.5.18"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -57,6 +57,7 @@ log = "0.4.14"
|
|||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
|
|
||||||
# Util
|
# Util
|
||||||
|
rand = "0.8.5"
|
||||||
ulid = "0.5.0"
|
ulid = "0.5.0"
|
||||||
regex = "1.5.5"
|
regex = "1.5.5"
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
|
|||||||
@@ -1,64 +1,12 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
pub static REGION_ID: Lazy<u16> = Lazy::new(|| env::var("REGION_ID")
|
pub static REGION_ID: Lazy<u16> = Lazy::new(|| {
|
||||||
|
env::var("REGION_ID")
|
||||||
.unwrap_or_else(|_| "0".to_string())
|
.unwrap_or_else(|_| "0".to_string())
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap());
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
pub static REGION_KEY: Lazy<String> = Lazy::new(|| format!("region{}", &*REGION_ID));
|
pub static REGION_KEY: Lazy<String> = Lazy::new(|| format!("region{}", &*REGION_ID));
|
||||||
|
|
||||||
/// Compact presence information for a user
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct PresenceEntry {
|
|
||||||
/// Region this session exists in
|
|
||||||
///
|
|
||||||
/// We can have up to 65535 regions
|
|
||||||
pub region_id: u16,
|
|
||||||
|
|
||||||
/// Unique session ID
|
|
||||||
pub session_id: u8,
|
|
||||||
|
|
||||||
/// Known flags about session
|
|
||||||
pub flags: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PresenceEntry {
|
|
||||||
/// Create a new presence entry from a given session ID and known flags
|
|
||||||
pub fn from(session_id: u8, flags: u8) -> Self {
|
|
||||||
Self {
|
|
||||||
region_id: *REGION_ID,
|
|
||||||
session_id,
|
|
||||||
flags,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PresenceOp {
|
|
||||||
/// Find next available session ID
|
|
||||||
fn find_next_id(&self) -> u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PresenceOp for Vec<PresenceEntry> {
|
|
||||||
fn find_next_id(&self) -> u8 {
|
|
||||||
// O(n^2) scan algorithm
|
|
||||||
// should be relatively fast at low numbers anyways
|
|
||||||
for i in 0..255 {
|
|
||||||
let mut found = false;
|
|
||||||
for entry in self {
|
|
||||||
if entry.session_id == i {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
255
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,87 +2,77 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
use redis_kiss::{get_connection, AsyncCommands};
|
use redis_kiss::{get_connection, AsyncCommands};
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
mod entry;
|
mod entry;
|
||||||
mod operations;
|
mod operations;
|
||||||
|
|
||||||
use entry::{PresenceEntry, PresenceOp};
|
|
||||||
use operations::{
|
use operations::{
|
||||||
__add_to_set_sessions, __delete_key_presence_entry, __get_key_presence_entry,
|
__add_to_set_string, __add_to_set_u32, __delete_key, __get_set_members_as_string,
|
||||||
__get_set_sessions, __remove_from_set_sessions, __set_key_presence_entry,
|
__get_set_size, __remove_from_set_string, __remove_from_set_u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::presence::operations::__delete_set_sessions;
|
|
||||||
|
|
||||||
use self::entry::REGION_KEY;
|
use self::entry::REGION_KEY;
|
||||||
|
|
||||||
/// Create a new presence session, returns the ID of this session
|
/// Create a new presence session, returns the ID of this session
|
||||||
pub async fn presence_create_session(user_id: &str, flags: u8) -> (bool, u8) {
|
pub async fn presence_create_session(user_id: &str, flags: u8) -> (bool, u32) {
|
||||||
info!("Creating a presence session for {user_id} with flags {flags}");
|
info!("Creating a presence session for {user_id} with flags {flags}");
|
||||||
|
|
||||||
// Try to find the presence entry for this user.
|
if let Ok(mut conn) = get_connection().await {
|
||||||
let mut conn = get_connection().await.unwrap();
|
// Check whether this is the first session
|
||||||
let mut entry: Vec<PresenceEntry> = __get_key_presence_entry(&mut conn, user_id)
|
let was_empty = __get_set_size(&mut conn, user_id).await == 0;
|
||||||
.await
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Return whether this was the first session.
|
// A session ID is comprised of random data and any flags ORed to the end
|
||||||
let was_empty = entry.is_empty();
|
let session_id = {
|
||||||
info!("User ID {} just came online.", &user_id);
|
let mut rng = rand::thread_rng();
|
||||||
|
(rng.gen::<u32>() ^ 1) | (flags as u32 & 1)
|
||||||
|
};
|
||||||
|
|
||||||
// Generate session ID and push new entry.
|
// Add session to user's sessions and to the region
|
||||||
let session_id = entry.find_next_id();
|
__add_to_set_u32(&mut conn, user_id, session_id).await;
|
||||||
entry.push(PresenceEntry::from(session_id, flags));
|
__add_to_set_string(&mut conn, ®ION_KEY, &format!("{user_id}:{session_id}")).await;
|
||||||
__set_key_presence_entry(&mut conn, user_id, entry).await;
|
info!("Created session for {user_id}, assigned them a session ID of {session_id}.");
|
||||||
|
|
||||||
// Add to region set in case of failure.
|
|
||||||
__add_to_set_sessions(&mut conn, ®ION_KEY, user_id, session_id).await;
|
|
||||||
(was_empty, session_id)
|
(was_empty, session_id)
|
||||||
|
} else {
|
||||||
|
// Fail through
|
||||||
|
(false, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete existing presence session
|
/// Delete existing presence session
|
||||||
pub async fn presence_delete_session(user_id: &str, session_id: u8) -> bool {
|
pub async fn presence_delete_session(user_id: &str, session_id: u32) -> bool {
|
||||||
presence_delete_session_internal(user_id, session_id, false).await
|
presence_delete_session_internal(user_id, session_id, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete existing presence session (but also choose whether to skip region)
|
/// Delete existing presence session (but also choose whether to skip region)
|
||||||
async fn presence_delete_session_internal(
|
async fn presence_delete_session_internal(
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
session_id: u8,
|
session_id: u32,
|
||||||
skip_region: bool,
|
skip_region: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
info!("Deleting presence session for {user_id} with id {session_id}");
|
info!("Deleting presence session for {user_id} with id {session_id}");
|
||||||
|
|
||||||
// Return whether this was the last session.
|
if let Ok(mut conn) = get_connection().await {
|
||||||
let mut is_empty = false;
|
// Remove the session
|
||||||
|
__remove_from_set_u32(&mut conn, user_id, session_id).await;
|
||||||
|
|
||||||
// Only continue if we can actually find one.
|
// Remove from the region
|
||||||
let mut conn = get_connection().await.unwrap();
|
|
||||||
let entry: Option<Vec<PresenceEntry>> = __get_key_presence_entry(&mut conn, user_id).await;
|
|
||||||
if let Some(entry) = entry {
|
|
||||||
let entries = entry
|
|
||||||
.into_iter()
|
|
||||||
.filter(|x| x.session_id != session_id)
|
|
||||||
.collect::<Vec<PresenceEntry>>();
|
|
||||||
|
|
||||||
// If entry is empty, then just delete it.
|
|
||||||
if entries.is_empty() {
|
|
||||||
__delete_key_presence_entry(&mut conn, user_id).await;
|
|
||||||
is_empty = true;
|
|
||||||
} else {
|
|
||||||
__set_key_presence_entry(&mut conn, user_id, entries).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from region set.
|
|
||||||
if !skip_region {
|
if !skip_region {
|
||||||
__remove_from_set_sessions(&mut conn, ®ION_KEY, user_id, session_id).await;
|
__remove_from_set_string(&mut conn, ®ION_KEY, &format!("{user_id}:{session_id}"))
|
||||||
}
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return whether this was the last session
|
||||||
|
let is_empty = __get_set_size(&mut conn, user_id).await == 0;
|
||||||
if is_empty {
|
if is_empty {
|
||||||
info!("User ID {} just went offline.", &user_id);
|
info!("User ID {} just went offline.", &user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
is_empty
|
is_empty
|
||||||
|
} else {
|
||||||
|
// Fail through
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether a given user ID is online
|
/// Check whether a given user ID is online
|
||||||
@@ -102,6 +92,10 @@ pub async fn presence_filter_online(user_ids: &'_ [String]) -> HashSet<String> {
|
|||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: at the point that we need mobile indicators
|
||||||
|
// you can interpret the data here and return a new data
|
||||||
|
// structure like HashMap<String /* id */, u8 /* flags */>
|
||||||
|
|
||||||
// We need to handle a special case where only one is present
|
// We need to handle a special case where only one is present
|
||||||
// as for some reason or another, Redis does not like us sending
|
// as for some reason or another, Redis does not like us sending
|
||||||
// a list of just one ID to the server.
|
// a list of just one ID to the server.
|
||||||
@@ -136,7 +130,7 @@ pub async fn presence_clear_region(region_id: Option<&str>) {
|
|||||||
let region_id = region_id.unwrap_or(&*REGION_KEY);
|
let region_id = region_id.unwrap_or(&*REGION_KEY);
|
||||||
let mut conn = get_connection().await.expect("Redis connection");
|
let mut conn = get_connection().await.expect("Redis connection");
|
||||||
|
|
||||||
let sessions = __get_set_sessions(&mut conn, region_id).await;
|
let sessions = __get_set_members_as_string(&mut conn, region_id).await;
|
||||||
if !sessions.is_empty() {
|
if !sessions.is_empty() {
|
||||||
info!(
|
info!(
|
||||||
"Cleaning up {} sessions, this may take a while...",
|
"Cleaning up {} sessions, this may take a while...",
|
||||||
@@ -155,7 +149,7 @@ pub async fn presence_clear_region(region_id: Option<&str>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then clear the set in Redis.
|
// Then clear the set in Redis.
|
||||||
__delete_set_sessions(&mut conn, region_id).await;
|
__delete_key(&mut conn, region_id).await;
|
||||||
|
|
||||||
info!("Clean up complete.");
|
info!("Clean up complete.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,42 @@
|
|||||||
use redis_kiss::{AsyncCommands, Conn};
|
use redis_kiss::{AsyncCommands, Conn};
|
||||||
|
|
||||||
use super::entry::PresenceEntry;
|
/// Add to set (string)
|
||||||
|
pub async fn __add_to_set_string(conn: &mut Conn, key: &str, value: &str) {
|
||||||
/// Set presence entry by given ID
|
let _: Option<()> = conn.sadd(key, value).await.ok();
|
||||||
pub async fn __set_key_presence_entry(conn: &mut Conn, id: &str, data: Vec<PresenceEntry>) {
|
|
||||||
let _: Option<()> = conn.set(id, bincode::serialize(&data).unwrap()).await.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete presence entry by given ID
|
/// Add to set (u32)
|
||||||
pub async fn __delete_key_presence_entry(conn: &mut Conn, id: &str) {
|
pub async fn __add_to_set_u32(conn: &mut Conn, key: &str, value: u32) {
|
||||||
let _: Option<()> = conn.del(id).await.ok();
|
let _: Option<()> = conn.sadd(key, value).await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get presence entry by given ID
|
/// Remove from set (string)
|
||||||
pub async fn __get_key_presence_entry(conn: &mut Conn, id: &str) -> Option<Vec<PresenceEntry>> {
|
pub async fn __remove_from_set_string(conn: &mut Conn, key: &str, value: &str) {
|
||||||
conn.get::<_, Option<Vec<u8>>>(id)
|
let _: Option<()> = conn.srem(key, value).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove from set (u32)
|
||||||
|
pub async fn __remove_from_set_u32(conn: &mut Conn, key: &str, value: u32) {
|
||||||
|
let _: Option<()> = conn.srem(key, value).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get set members as string
|
||||||
|
pub async fn __get_set_members_as_string(conn: &mut Conn, key: &str) -> Vec<String> {
|
||||||
|
conn.smembers::<_, Vec<String>>(key)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.expect("could not get set members as string")
|
||||||
.map(|entry| bincode::deserialize(&entry[..]).unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add to region session set
|
/// Get set size
|
||||||
pub async fn __add_to_set_sessions(
|
pub async fn __get_set_size(conn: &mut Conn, id: &str) -> u32 {
|
||||||
conn: &mut Conn,
|
conn.scard::<_, u32>(id)
|
||||||
region_id: &str,
|
|
||||||
user_id: &str,
|
|
||||||
session_id: u8,
|
|
||||||
) {
|
|
||||||
let _: Option<()> = conn
|
|
||||||
.sadd(region_id, format!("{user_id}:{session_id}"))
|
|
||||||
.await
|
.await
|
||||||
.ok();
|
.expect("could not get set size")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove from region session set
|
/// Delete key by id
|
||||||
pub async fn __remove_from_set_sessions(
|
pub async fn __delete_key(conn: &mut Conn, id: &str) {
|
||||||
conn: &mut Conn,
|
conn.del::<_, ()>(id)
|
||||||
region_id: &str,
|
|
||||||
user_id: &str,
|
|
||||||
session_id: u8,
|
|
||||||
) {
|
|
||||||
let _: Option<()> = conn
|
|
||||||
.srem(region_id, format!("{user_id}:{session_id}"))
|
|
||||||
.await
|
.await
|
||||||
.ok();
|
.expect("could not delete key by id");
|
||||||
}
|
|
||||||
|
|
||||||
/// Get region session set as list
|
|
||||||
pub async fn __get_set_sessions(conn: &mut Conn, region_id: &str) -> Vec<String> {
|
|
||||||
conn.smembers::<_, Vec<String>>(region_id).await.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete region session set
|
|
||||||
pub async fn __delete_set_sessions(conn: &mut Conn, region_id: &str) {
|
|
||||||
conn.del::<_, ()>(region_id).await.unwrap();
|
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ pub fn setup_logging(release: &'static str) -> sentry::ClientInitGuard {
|
|||||||
info!("Starting {release}");
|
info!("Starting {release}");
|
||||||
|
|
||||||
sentry::init((
|
sentry::init((
|
||||||
"https://62fd0e02c5354905b4e286757f4beb16@sentry.insert.moe/4",
|
"https://d1d2a6f15c6245a987c532bbbcb30a04@glitchtip.insert.moe/2",
|
||||||
sentry::ClientOptions {
|
sentry::ClientOptions {
|
||||||
release: Some(release.into()),
|
release: Some(release.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use revolt_rocket_okapi::revolt_okapi::openapi3;
|
|||||||
use rocket::{
|
use rocket::{
|
||||||
http::{ContentType, Status},
|
http::{ContentType, Status},
|
||||||
response::{self, Responder},
|
response::{self, Responder},
|
||||||
serde::json::serde_json::json,
|
|
||||||
Request, Response,
|
Request, Response,
|
||||||
};
|
};
|
||||||
use schemars::schema::Schema;
|
use schemars::schema::Schema;
|
||||||
@@ -39,8 +38,15 @@ pub enum Error {
|
|||||||
UnknownMessage,
|
UnknownMessage,
|
||||||
CannotEditMessage,
|
CannotEditMessage,
|
||||||
CannotJoinCall,
|
CannotJoinCall,
|
||||||
TooManyAttachments,
|
TooManyAttachments {
|
||||||
TooManyReplies,
|
max: usize
|
||||||
|
},
|
||||||
|
TooManyReplies {
|
||||||
|
max: usize
|
||||||
|
},
|
||||||
|
TooManyChannels {
|
||||||
|
max: usize
|
||||||
|
},
|
||||||
EmptyMessage,
|
EmptyMessage,
|
||||||
PayloadTooLarge,
|
PayloadTooLarge,
|
||||||
CannotRemoveYourself,
|
CannotRemoveYourself,
|
||||||
@@ -57,7 +63,12 @@ pub enum Error {
|
|||||||
TooManyServers {
|
TooManyServers {
|
||||||
max: usize,
|
max: usize,
|
||||||
},
|
},
|
||||||
TooManyEmoji,
|
TooManyEmoji {
|
||||||
|
max: usize
|
||||||
|
},
|
||||||
|
TooManyRoles {
|
||||||
|
max: usize
|
||||||
|
},
|
||||||
|
|
||||||
// ? Bot related errors
|
// ? Bot related errors
|
||||||
ReachedMaximumBots,
|
ReachedMaximumBots,
|
||||||
@@ -151,8 +162,8 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
Error::UnknownAttachment => Status::BadRequest,
|
Error::UnknownAttachment => Status::BadRequest,
|
||||||
Error::CannotEditMessage => Status::Forbidden,
|
Error::CannotEditMessage => Status::Forbidden,
|
||||||
Error::CannotJoinCall => Status::BadRequest,
|
Error::CannotJoinCall => Status::BadRequest,
|
||||||
Error::TooManyAttachments => Status::BadRequest,
|
Error::TooManyAttachments { .. } => Status::BadRequest,
|
||||||
Error::TooManyReplies => Status::BadRequest,
|
Error::TooManyReplies { .. } => Status::BadRequest,
|
||||||
Error::EmptyMessage => Status::UnprocessableEntity,
|
Error::EmptyMessage => Status::UnprocessableEntity,
|
||||||
Error::PayloadTooLarge => Status::UnprocessableEntity,
|
Error::PayloadTooLarge => Status::UnprocessableEntity,
|
||||||
Error::CannotRemoveYourself => Status::BadRequest,
|
Error::CannotRemoveYourself => Status::BadRequest,
|
||||||
@@ -163,8 +174,11 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
Error::UnknownServer => Status::NotFound,
|
Error::UnknownServer => Status::NotFound,
|
||||||
Error::InvalidRole => Status::NotFound,
|
Error::InvalidRole => Status::NotFound,
|
||||||
Error::Banned => Status::Forbidden,
|
Error::Banned => Status::Forbidden,
|
||||||
Error::TooManyServers { .. } => Status::Forbidden,
|
|
||||||
Error::TooManyEmoji => Status::BadRequest,
|
Error::TooManyServers { .. } => Status::BadRequest,
|
||||||
|
Error::TooManyEmoji { .. } => Status::BadRequest,
|
||||||
|
Error::TooManyChannels { .. } => Status::BadRequest,
|
||||||
|
Error::TooManyRoles { .. } => Status::BadRequest,
|
||||||
|
|
||||||
Error::ReachedMaximumBots => Status::BadRequest,
|
Error::ReachedMaximumBots => Status::BadRequest,
|
||||||
Error::IsBot => Status::BadRequest,
|
Error::IsBot => Status::BadRequest,
|
||||||
@@ -193,7 +207,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Serialize the error data structure into JSON.
|
// Serialize the error data structure into JSON.
|
||||||
let string = json!(self).to_string();
|
let string = serde_json::to_string(&self).unwrap();
|
||||||
|
|
||||||
// Build and send the request.
|
// Build and send the request.
|
||||||
Response::build()
|
Response::build()
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ pub static MAX_GROUP_SIZE: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_GROUP
|
|||||||
pub static MAX_BOT_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_BOT_COUNT").unwrap_or_else(|_| "5".to_string()).parse().unwrap());
|
pub static MAX_BOT_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_BOT_COUNT").unwrap_or_else(|_| "5".to_string()).parse().unwrap());
|
||||||
pub static MAX_EMBED_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_EMBED_COUNT").unwrap_or_else(|_| "5".to_string()).parse().unwrap());
|
pub static MAX_EMBED_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_EMBED_COUNT").unwrap_or_else(|_| "5".to_string()).parse().unwrap());
|
||||||
pub static MAX_SERVER_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_SERVER_COUNT").unwrap_or_else(|_| "100".to_string()).parse().unwrap());
|
pub static MAX_SERVER_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_SERVER_COUNT").unwrap_or_else(|_| "100".to_string()).parse().unwrap());
|
||||||
|
pub static MAX_CHANNEL_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_CHANNEL_COUNT").unwrap_or_else(|_| "200".to_string()).parse().unwrap());
|
||||||
|
pub static MAX_ROLE_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_ROLE_COUNT").unwrap_or_else(|_| "200".to_string()).parse().unwrap());
|
||||||
|
pub static MAX_EMOJI_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_EMOJI_COUNT").unwrap_or_else(|_| "100".to_string()).parse().unwrap());
|
||||||
|
pub static MAX_ATTACHMENT_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_ATTACHMENT_COUNT").unwrap_or_else(|_| "5".to_string()).parse().unwrap());
|
||||||
|
pub static MAX_REPLY_COUNT: Lazy<usize> = Lazy::new(|| env::var("REVOLT_MAX_REPLY_COUNT").unwrap_or_else(|_| "5".to_string()).parse().unwrap());
|
||||||
|
|
||||||
pub static EARLY_ADOPTER_BADGE: Lazy<i64> = Lazy::new(|| env::var("REVOLT_EARLY_ADOPTER_BADGE").unwrap_or_else(|_| "0".to_string()).parse().unwrap());
|
pub static EARLY_ADOPTER_BADGE: Lazy<i64> = Lazy::new(|| env::var("REVOLT_EARLY_ADOPTER_BADGE").unwrap_or_else(|_| "0".to_string()).parse().unwrap());
|
||||||
|
|
||||||
pub fn preflight_checks() {
|
pub fn preflight_checks() {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
version: "3.3"
|
version: "3.3"
|
||||||
services:
|
services:
|
||||||
|
# Redis
|
||||||
|
redis:
|
||||||
|
image: eqalpha/keydb
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
|
||||||
# MongoDB
|
# MongoDB
|
||||||
database:
|
database:
|
||||||
image: mongo
|
image: mongo
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
case "${TARGETARCH}" in
|
||||||
|
"amd64")
|
||||||
|
LINKER_NAME="x86_64-linux-gnu-gcc"
|
||||||
|
LINKER_PACKAGE="gcc-x86-64-linux-gnu"
|
||||||
|
BUILD_TARGET="x86_64-unknown-linux-gnu" ;;
|
||||||
|
"arm64")
|
||||||
|
LINKER_NAME="aarch64-linux-gnu-gcc"
|
||||||
|
LINKER_PACKAGE="gcc-aarch64-linux-gnu"
|
||||||
|
BUILD_TARGET="aarch64-unknown-linux-gnu" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
tools() {
|
||||||
|
apt-get install -y "${LINKER_PACKAGE}"
|
||||||
|
rustup target add "${BUILD_TARGET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
deps() {
|
||||||
|
mkdir -p \
|
||||||
|
crates/bonfire/src \
|
||||||
|
crates/delta/src \
|
||||||
|
crates/quark/src
|
||||||
|
echo 'fn main() { panic!("stub"); }' |
|
||||||
|
tee crates/bonfire/src/main.rs |
|
||||||
|
tee crates/delta/src/main.rs
|
||||||
|
echo '' |
|
||||||
|
tee crates/quark/src/lib.rs
|
||||||
|
cargo build --locked --release --target "${BUILD_TARGET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
apps() {
|
||||||
|
touch -am \
|
||||||
|
crates/bonfire/src/main.rs \
|
||||||
|
crates/delta/src/main.rs \
|
||||||
|
crates/quark/src/lib.rs
|
||||||
|
cargo build --locked --release --target "${BUILD_TARGET}"
|
||||||
|
mv target _target && mv _target/"${BUILD_TARGET}" target
|
||||||
|
}
|
||||||
|
|
||||||
|
export RUSTFLAGS="-C linker=${LINKER_NAME}"
|
||||||
|
export PKG_CONFIG_ALLOW_CROSS="1"
|
||||||
|
export PKG_CONFIG_PATH="/usr/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig"
|
||||||
|
|
||||||
|
"$@"
|
||||||
Reference in new issue
Block a user