feat: add fluxer upstream source and self-hosting documentation

- Clone of github.com/fluxerapp/fluxer (official upstream)
- SELF_HOSTING.md: full VM rebuild procedure, architecture overview,
  service reference, step-by-step setup, troubleshooting, seattle reference
- dev/.env.example: all env vars with secrets redacted and generation instructions
- dev/livekit.yaml: LiveKit config template with placeholder keys
- fluxer-seattle/: existing seattle deployment setup scripts
This commit is contained in:
Vish
2026-03-13 00:55:14 -07:00
parent 5ceda343b8
commit 3b9d759b4b
5859 changed files with 1923440 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
{
servers {
listener_wrappers {
proxy_protocol {
timeout 5s
allow 127.0.0.0/8
allow 10.0.0.0/8
allow 172.16.0.0/12
allow ::1/128
}
tls
}
trusted_proxies static private_ranges
trusted_proxies_strict
}
}

View File

@@ -0,0 +1,38 @@
services:
caddy-gateway:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
environment:
- CADDY_INGRESS_NETWORKS=fluxer-shared
- CADDY_DOCKER_LABEL_PREFIX=caddy_gw
- CADDY_DOCKER_CADDYFILE_PATH=/config/Caddyfile.base
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- caddy_gateway_data:/data
configs:
- source: caddyfile_config
target: /config/Caddyfile.base
networks:
- fluxer-shared
ports:
- target: 443
published: 9443
protocol: tcp
mode: host
deploy:
mode: global
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
configs:
caddyfile_config:
file: ./Caddyfile.global
networks:
fluxer-shared:
external: true
volumes:
caddy_gateway_data:

View File

@@ -0,0 +1,16 @@
{
servers {
listener_wrappers {
proxy_protocol {
timeout 5s
allow 127.0.0.0/8
allow 10.0.0.0/8
allow 172.16.0.0/12
allow ::1/128
}
tls
}
trusted_proxies static private_ranges
trusted_proxies_strict
}
}

View File

@@ -0,0 +1,41 @@
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
environment:
- CADDY_INGRESS_NETWORKS=fluxer-shared
- CADDY_DOCKER_CADDYFILE_PATH=/config/Caddyfile.base
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- caddy_data:/data
configs:
- source: caddyfile_config
target: /config/Caddyfile.base
networks:
- fluxer-shared
ports:
- target: 80
published: 8080
protocol: tcp
mode: host
- target: 443
published: 8443
protocol: tcp
mode: host
deploy:
mode: global
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
configs:
caddyfile_config:
file: ./Caddyfile.global
networks:
fluxer-shared:
external: true
volumes:
caddy_data:

View File

@@ -0,0 +1,42 @@
FROM cassandra:5.0
# Install backup tools only
RUN apt-get update && apt-get install -y \
age \
awscli \
&& rm -rf /var/lib/apt/lists/*
# Copy backup script
COPY backup.sh /usr/local/bin/backup.sh
RUN chmod +x /usr/local/bin/backup.sh
# Create entrypoint that runs backups in a loop
RUN echo '#!/bin/bash\n\
set -e\n\
\n\
# Create age public key file from environment variable\n\
if [ -n "${AGE_PUBLIC_KEY}" ]; then\n\
echo "${AGE_PUBLIC_KEY}" > /tmp/age_public_key.txt\n\
chmod 644 /tmp/age_public_key.txt\n\
echo "Age encryption enabled for backups"\n\
else\n\
echo "Warning: AGE_PUBLIC_KEY not set - backups will not be encrypted"\n\
fi\n\
\n\
echo "Starting backup service - first backup in 5 minutes, then hourly"\n\
\n\
# Wait 5 minutes before first backup\n\
echo "Waiting 5 minutes for Cassandra to be ready..."\n\
sleep 300\n\
\n\
# Run backups in a loop\n\
while true; do\n\
echo "-----------------------------------"\n\
echo "Starting backup at $(date)"\n\
/usr/local/bin/backup.sh || echo "Backup failed at $(date)"\n\
echo "Next backup in 1 hour"\n\
sleep 3600\n\
done\n\
' > /usr/local/bin/backup-entrypoint.sh && chmod +x /usr/local/bin/backup-entrypoint.sh
ENTRYPOINT ["/usr/local/bin/backup-entrypoint.sh"]

View File

@@ -0,0 +1,146 @@
# Cassandra Restore
## Fresh Instance from Local Backup
```bash
# 1. Create volume and start Cassandra
docker volume create cassandra_data
docker run -d --name cass -v cassandra_data:/var/lib/cassandra -p 9042:9042 cassandra:5.0
echo "Waiting for Cassandra to start..."
sleep 30
# 2. Extract backup and apply schema
docker exec cass sh -c 'apt-get update -qq && apt-get install -y -qq age'
docker cp ~/Downloads/backup.tar.age cass:/tmp/
docker cp ~/Downloads/key.txt cass:/tmp/
docker exec cass sh -c 'age -d -i /tmp/key.txt /tmp/backup.tar.age | tar -C /tmp -xf -'
docker exec cass sh -c 'sed "/^WARNING:/d" /tmp/cassandra-backup-*/schema.cql | cqlsh'
# 3. Copy backup to volume and stop Cassandra
docker exec cass sh -c 'cp -r /tmp/cassandra-backup-* /var/lib/cassandra/'
docker stop cass
docker run -d --name cass-util -v cassandra_data:/var/lib/cassandra --entrypoint sleep cassandra:5.0 infinity
docker exec cass-util sh -c '
BACKUP_DIR=$(ls -d /var/lib/cassandra/cassandra-backup-* | head -1)
DATA_DIR=/var/lib/cassandra/data
for keyspace_dir in "$BACKUP_DIR"/*/; do
keyspace=$(basename "$keyspace_dir")
[[ "$keyspace" =~ ^system ]] && continue
[ ! -d "$keyspace_dir" ] && continue
for snapshot_dir in "$keyspace_dir"/*/snapshots/backup-*/; do
[ ! -d "$snapshot_dir" ] && continue
table_with_uuid=$(basename $(dirname $(dirname "$snapshot_dir")))
table_name=$(echo "$table_with_uuid" | cut -d- -f1)
target_dir=$(ls -d "$DATA_DIR/$keyspace/${table_name}"-* 2>/dev/null | head -1)
if [ -n "$target_dir" ]; then
cp "$snapshot_dir"/* "$target_dir"/ 2>/dev/null || true
fi
done
done
chown -R cassandra:cassandra "$DATA_DIR"
'
# 4. Restart Cassandra and refresh tables
docker rm -f cass-util
docker start cass
sleep 30
# 5. Run nodetool refresh on all tables
docker exec cass sh -c '
BACKUP_DIR=$(ls -d /var/lib/cassandra/cassandra-backup-* | head -1)
for keyspace_dir in "$BACKUP_DIR"/*/; do
keyspace=$(basename "$keyspace_dir")
[[ "$keyspace" =~ ^system ]] && continue
for snapshot_dir in "$keyspace_dir"/*/snapshots/backup-*/; do
[ ! -d "$snapshot_dir" ] && continue
table_with_uuid=$(basename $(dirname $(dirname "$snapshot_dir")))
table_name=$(echo "$table_with_uuid" | cut -d- -f1)
nodetool refresh -- "$keyspace" "$table_name" 2>&1 | grep -v deprecated || true
done
done
'
# 6. Verify
docker exec cass cqlsh -e "SELECT COUNT(*) FROM fluxer.users;"
```
## Production Restore from B2
> [!IMPORTANT]
> This assumes you have B2 credentials configured on the server.
```bash
# 0. Set variables
BACKUP_NAME="cassandra-backup-20251016-103753.tar.age" # Replace with actual backup name
CASSANDRA_CONTAINER="cassandra-prod"
# 1. Download backup from B2 (on the server)
export AWS_ACCESS_KEY_ID="${B2_KEY_ID}"
export AWS_SECRET_ACCESS_KEY="${B2_APPLICATION_KEY}"
export AWS_DEFAULT_REGION="${B2_REGION}"
B2_ENDPOINT_URL="https://${B2_ENDPOINT}"
aws s3 cp "s3://${B2_BUCKET_NAME}/${BACKUP_NAME}" \
"/tmp/${BACKUP_NAME}" \
--endpoint-url="${B2_ENDPOINT_URL}"
# 2. Copy backup and key to Cassandra container
docker cp "/tmp/${BACKUP_NAME}" ${CASSANDRA_CONTAINER}:/tmp/
docker cp /etc/cassandra/age_private_key.txt ${CASSANDRA_CONTAINER}:/tmp/key.txt
# 3. Stop Cassandra and prepare
docker exec ${CASSANDRA_CONTAINER} sh -c 'apt-get update -qq && apt-get install -y -qq age'
docker stop ${CASSANDRA_CONTAINER}
# 4. Extract backup in utility container
docker run -d --name cass-restore-util --volumes-from ${CASSANDRA_CONTAINER} --entrypoint sleep cassandra:5.0 infinity
docker exec cass-restore-util sh -c 'age -d -i /tmp/key.txt /tmp/${BACKUP_NAME} | tar -C /tmp -xf -'
docker exec cass-restore-util sh -c 'cp -r /tmp/cassandra-backup-* /var/lib/cassandra/'
# 5. Copy SSTable files to existing schema directories
docker exec cass-restore-util sh -c '
BACKUP_DIR=$(ls -d /var/lib/cassandra/cassandra-backup-* | head -1)
DATA_DIR=/var/lib/cassandra/data
for keyspace_dir in "$BACKUP_DIR"/*/; do
keyspace=$(basename "$keyspace_dir")
[[ "$keyspace" =~ ^system ]] && continue
[ ! -d "$keyspace_dir" ] && continue
for snapshot_dir in "$keyspace_dir"/*/snapshots/backup-*/; do
[ ! -d "$snapshot_dir" ] && continue
table_with_uuid=$(basename $(dirname $(dirname "$snapshot_dir")))
table_name=$(echo "$table_with_uuid" | cut -d- -f1)
target_dir=$(ls -d "$DATA_DIR/$keyspace/${table_name}"-* 2>/dev/null | head -1)
if [ -n "$target_dir" ]; then
cp "$snapshot_dir"/* "$target_dir"/ 2>/dev/null || true
fi
done
done
chown -R cassandra:cassandra "$DATA_DIR"
'
# 6. Restart Cassandra
docker rm -f cass-restore-util
docker start ${CASSANDRA_CONTAINER}
sleep 30
# 7. Run nodetool refresh
docker exec ${CASSANDRA_CONTAINER} sh -c '
BACKUP_DIR=$(ls -d /var/lib/cassandra/cassandra-backup-* | head -1)
for keyspace_dir in "$BACKUP_DIR"/*/; do
keyspace=$(basename "$keyspace_dir")
[[ "$keyspace" =~ ^system ]] && continue
for snapshot_dir in "$keyspace_dir"/*/snapshots/backup-*/; do
[ ! -d "$snapshot_dir" ] && continue
table_with_uuid=$(basename $(dirname $(dirname "$snapshot_dir")))
table_name=$(echo "$table_with_uuid" | cut -d- -f1)
nodetool refresh -- "$keyspace" "$table_name" 2>&1 | grep -v deprecated || true
done
done
'
# 8. Verify
docker exec ${CASSANDRA_CONTAINER} cqlsh -e "SELECT COUNT(*) FROM fluxer.users;"
# 9. Cleanup
rm -f "/tmp/${BACKUP_NAME}"
```

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env sh
# Copyright (C) 2026 Fluxer Contributors
#
# This file is part of Fluxer.
#
# Fluxer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Fluxer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
set -eu
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_NAME="cassandra-backup-${TIMESTAMP}"
SNAPSHOT_TAG="backup-${TIMESTAMP}"
DATA_DIR="/var/lib/cassandra/data"
TEMP_DIR="/tmp/${BACKUP_NAME}"
AGE_PUBLIC_KEY_FILE="${AGE_PUBLIC_KEY_FILE:-/tmp/age_public_key.txt}"
ENCRYPTED_BACKUP="${BACKUP_NAME}.tar.age"
MAX_BACKUP_COUNT=168 # 7 days of hourly backups
CASSANDRA_HOST="${CASSANDRA_HOST:-cassandra}"
# AWS CLI configuration for B2
export AWS_ACCESS_KEY_ID="${B2_KEY_ID}"
export AWS_SECRET_ACCESS_KEY="${B2_APPLICATION_KEY}"
export AWS_DEFAULT_REGION="${B2_REGION}"
B2_ENDPOINT_URL="https://${B2_ENDPOINT}"
echo "[$(date)] Starting Cassandra backup: ${BACKUP_NAME}"
# Step 1: Create snapshot
echo "[$(date)] Creating Cassandra snapshot: ${SNAPSHOT_TAG}"
if ! nodetool -h "${CASSANDRA_HOST}" snapshot -t "${SNAPSHOT_TAG}"; then
echo "[$(date)] Error: Failed to create snapshot"
exit 1
fi
echo "[$(date)] Snapshot created successfully"
# Step 2: Collect snapshot files
echo "[$(date)] Collecting snapshot files"
mkdir -p "${TEMP_DIR}"
# Find all snapshot directories and copy to temp location
find "${DATA_DIR}" -type d -name "${SNAPSHOT_TAG}" | while IFS= read -r snapshot_dir; do
# Get relative path from data dir
rel_path=$(dirname "${snapshot_dir#$DATA_DIR/}")
target_dir="${TEMP_DIR}/${rel_path}"
mkdir -p "${target_dir}"
cp -r "${snapshot_dir}" "${target_dir}/"
done
# Copy schema
echo "[$(date)] Saving schema"
if [ -n "${CASSANDRA_PASSWORD:-}" ]; then
if ! cqlsh -u cassandra -p "${CASSANDRA_PASSWORD}" "${CASSANDRA_HOST}" -e "DESC SCHEMA;" 2>/dev/null | sed '/^WARNING:/d' > "${TEMP_DIR}/schema.cql"; then
echo "Warning: Could not export schema"
fi
else
if ! cqlsh "${CASSANDRA_HOST}" -e "DESC SCHEMA;" 2>/dev/null | sed '/^WARNING:/d' > "${TEMP_DIR}/schema.cql"; then
echo "Warning: Could not export schema (no password set)"
fi
fi
# Save cluster topology info
nodetool -h "${CASSANDRA_HOST}" describecluster > "${TEMP_DIR}/cluster_topology.txt" 2>/dev/null || true
nodetool -h "${CASSANDRA_HOST}" status > "${TEMP_DIR}/cluster_status.txt" 2>/dev/null || true
echo "[$(date)] Snapshot collection completed"
# Step 3: Check if encryption is enabled
if [ ! -f "${AGE_PUBLIC_KEY_FILE}" ]; then
echo "[$(date)] Warning: Age public key not found - skipping encryption and upload"
echo "[$(date)] Backup stored locally at: ${TEMP_DIR}"
# Clear snapshot from Cassandra
nodetool -h "${CASSANDRA_HOST}" clearsnapshot -t "${SNAPSHOT_TAG}"
exit 0
fi
# Step 4: Create tar archive and encrypt with age (streaming)
echo "[$(date)] Encrypting backup with age..."
if ! tar -C /tmp -cf - "${BACKUP_NAME}" | \
age -r "$(cat "${AGE_PUBLIC_KEY_FILE}")" -o "/tmp/${ENCRYPTED_BACKUP}"; then
echo "[$(date)] Error: Encryption failed"
rm -rf "${TEMP_DIR}"
nodetool -h "${CASSANDRA_HOST}" clearsnapshot -t "${SNAPSHOT_TAG}"
exit 1
fi
echo "[$(date)] Encryption completed: ${ENCRYPTED_BACKUP}"
# Get file size
BACKUP_SIZE=$(du -h "/tmp/${ENCRYPTED_BACKUP}" | cut -f1)
echo "[$(date)] Encrypted backup size: ${BACKUP_SIZE}"
# Step 5: Upload encrypted backup to B2
echo "[$(date)] Uploading encrypted backup to B2..."
if ! aws s3 cp "/tmp/${ENCRYPTED_BACKUP}" \
"s3://${B2_BUCKET_NAME}/${ENCRYPTED_BACKUP}" \
--endpoint-url="${B2_ENDPOINT_URL}"; then
echo "[$(date)] Error: Upload to B2 failed"
rm -f "/tmp/${ENCRYPTED_BACKUP}"
rm -rf "${TEMP_DIR}"
nodetool -h "${CASSANDRA_HOST}" clearsnapshot -t "${SNAPSHOT_TAG}"
exit 1
fi
echo "[$(date)] Upload completed successfully"
# Step 6: Cleanup
echo "[$(date)] Cleaning up temporary files..."
rm -f "/tmp/${ENCRYPTED_BACKUP}"
rm -rf "${TEMP_DIR}"
# Clear snapshot from Cassandra
echo "[$(date)] Clearing snapshot from Cassandra"
nodetool -h "${CASSANDRA_HOST}" clearsnapshot -t "${SNAPSHOT_TAG}"
# Step 7: Purge old backups from B2
echo "[$(date)] Purging old backups from B2 (keeping last ${MAX_BACKUP_COUNT})..."
aws s3 ls "s3://${B2_BUCKET_NAME}/" --endpoint-url="${B2_ENDPOINT_URL}" | \
grep "cassandra-backup-.*\.tar\.age$" | \
awk '{print $4}' | \
sort -r | \
tail -n +$((MAX_BACKUP_COUNT + 1)) | \
while IFS= read -r old_backup; do
echo "[$(date)] Deleting old backup: ${old_backup}"
aws s3 rm "s3://${B2_BUCKET_NAME}/${old_backup}" --endpoint-url="${B2_ENDPOINT_URL}" || true
done
echo "[$(date)] Backup process completed successfully"
echo "[$(date)] Backup name: ${ENCRYPTED_BACKUP}"
echo "[$(date)] Backup size: ${BACKUP_SIZE}"

View File

@@ -0,0 +1,95 @@
services:
cassandra:
image: ${CASSANDRA_IMAGE:-cassandra:5.0}
hostname: cassandra
env_file:
- .env
environment:
- CASSANDRA_CLUSTER_NAME=fluxer-cluster
- CASSANDRA_DC=dc1
- CASSANDRA_RACK=rack1
- CASSANDRA_ENDPOINT_SNITCH=GossipingPropertyFileSnitch
- CASSANDRA_SEEDS=cassandra
- MAX_HEAP_SIZE=32G
- CASSANDRA_BROADCAST_ADDRESS=cassandra
- CASSANDRA_LISTEN_ADDRESS=auto
- CASSANDRA_RPC_ADDRESS=0.0.0.0
- CASSANDRA_AUTHENTICATOR=PasswordAuthenticator
- CASSANDRA_AUTHORIZER=CassandraAuthorizer
volumes:
- cassandra_data:/var/lib/cassandra
- ./conf/cassandra.yaml:/etc/cassandra/cassandra.yaml
- ./conf/jvm-server.options:/etc/cassandra/jvm-server.options
networks:
- fluxer-shared
ports:
- target: 9042
published: 9042
protocol: tcp
mode: host
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 30s
max_attempts: 3
resources:
limits:
cpus: '6'
memory: 64G
reservations:
cpus: '4'
memory: 48G
healthcheck:
test: ['CMD-SHELL', 'nodetool status']
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 100000
hard: 100000
cap_add:
- IPC_LOCK
cassandra-backup:
image: ${CASSANDRA_BACKUP_IMAGE:-fluxer-cassandra-backup:latest}
environment:
- AGE_PUBLIC_KEY
- B2_KEY_ID
- B2_APPLICATION_KEY
- B2_BUCKET_NAME
- B2_ENDPOINT
- B2_REGION
- CASSANDRA_PASSWORD
volumes:
- cassandra_data:/var/lib/cassandra:ro
networks:
- fluxer-shared
depends_on:
- cassandra
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 30s
max_attempts: 3
resources:
limits:
cpus: '1'
memory: 2G
reservations:
cpus: '0.5'
memory: 1G
networks:
fluxer-shared:
external: true
volumes:
cassandra_data:
driver: local

View File

@@ -0,0 +1,69 @@
cluster_name: 'fluxer-cluster'
num_tokens: 256
partitioner: org.apache.cassandra.dht.Murmur3Partitioner
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
parameters:
- seeds: 'cassandra'
listen_address: auto
broadcast_address: cassandra
rpc_address: 0.0.0.0
broadcast_rpc_address: cassandra
storage_port: 7000
ssl_storage_port: 7001
native_transport_port: 9042
endpoint_snitch: GossipingPropertyFileSnitch
data_file_directories:
- /var/lib/cassandra/data
commitlog_directory: /var/lib/cassandra/commitlog
saved_caches_directory: /var/lib/cassandra/saved_caches
hints_directory: /var/lib/cassandra/hints
concurrent_reads: 32
concurrent_writes: 32
concurrent_counter_writes: 32
concurrent_materialized_view_writes: 32
memtable_allocation_type: heap_buffers
memtable_flush_writers: 4
commitlog_sync: periodic
commitlog_sync_period: 10000ms
commitlog_segment_size: 32MiB
commitlog_total_space: 8192MiB
compaction_throughput: 64MiB/s
concurrent_compactors: 4
sstable_preemptive_open_interval: 50MiB
key_cache_size: 2048MiB
key_cache_save_period: 14400
row_cache_size: 0MiB
counter_cache_size: 256MiB
counter_cache_save_period: 7200
read_request_timeout: 10000ms
write_request_timeout: 10000ms
counter_write_request_timeout: 10000ms
range_request_timeout: 20000ms
request_timeout: 20000ms
authenticator: PasswordAuthenticator
authorizer: CassandraAuthorizer
role_manager: CassandraRoleManager
native_transport_max_threads: 128
native_transport_max_frame_size: 256MiB
auto_snapshot: true
snapshot_before_compaction: false
tombstone_warn_threshold: 10000
tombstone_failure_threshold: 100000
disk_optimization_strategy: ssd
disk_access_mode: auto
stream_throughput_outbound: 400MiB/s
inter_dc_stream_throughput_outbound: 200MiB/s
max_hints_delivery_threads: 4
hints_flush_period: 10000ms
gc_warn_threshold: 1000ms
gc_log_threshold: 200ms
batch_size_warn_threshold: 5KiB
batch_size_fail_threshold: 50KiB
prepared_statements_cache_size: 10MiB
client_encryption_options:
enabled: false
optional: false
server_encryption_options:
internode_encryption: none
optional: false

View File

@@ -0,0 +1,48 @@
-Xms32G
-Xmx32G
-XX:+UseG1GC
-XX:+ParallelRefProcEnabled
-XX:MaxTenuringThreshold=2
-XX:G1HeapRegionSize=16m
-XX:G1RSetUpdatingPauseTimePercent=5
-XX:MaxGCPauseMillis=300
-XX:InitiatingHeapOccupancyPercent=70
-Xlog:gc*,gc+age=trace,safepoint:file=/var/log/cassandra/gc.log:time,uptime,level,tags:filecount=10,filesize=100M
-XX:+AlwaysPreTouch
-XX:+IgnoreUnrecognizedVMOptions
-Xss512k
-XX:+UseTLAB
-XX:+ResizeTLAB
-XX:+PerfDisableSharedMem
-XX:+UseStringDeduplication
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/lib/cassandra/dumps
-Djdk.attach.allowAttachSelf=true
--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED
--add-exports=java.base/jdk.internal.ref=ALL-UNNAMED
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED
--add-exports=java.management.rmi/com.sun.jmx.remote.internal.rmi=ALL-UNNAMED
--add-exports=java.rmi/sun.rmi.registry=ALL-UNNAMED
--add-exports=java.rmi/sun.rmi.server=ALL-UNNAMED
--add-exports=java.sql/java.sql=ALL-UNNAMED
--add-opens=java.base/java.lang.module=ALL-UNNAMED
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED
--add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED
--add-opens=java.base/jdk.internal.math=ALL-UNNAMED
--add-opens=java.base/jdk.internal.module=ALL-UNNAMED
--add-opens=java.base/jdk.internal.util.jar=ALL-UNNAMED
--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcassandra.jmx.local.port=7199
-Dcassandra.jmx.remote.port=7199
-Djava.net.preferIPv4Stack=true
-Dio.netty.tryReflectionSetAccessible=true
-Dio.netty.allocator.useCacheForAllThreads=true
-Dio.netty.eventLoop.maxPendingTasks=65536
-Dcassandra.config=file:///etc/cassandra/cassandra.yaml
-Dcassandra.logdir=/var/log/cassandra
-Dcassandra.storagedir=/var/lib/cassandra
-Djava.security.egd=file:/dev/urandom
-Dfile.encoding=UTF-8

View File

@@ -0,0 +1,919 @@
CREATE TABLE IF NOT EXISTS fluxer.users (
user_id bigint,
username text,
discriminator int,
bot boolean,
system boolean,
email text,
email_verified boolean,
email_bounced boolean,
phone text,
password_hash text,
totp_secret text,
authenticator_types set<int>,
avatar_hash text,
banner_hash text,
bio text,
accent_color int,
date_of_birth date,
locale text,
flags bigint,
premium_type int,
premium_since timestamp,
premium_until timestamp,
premium_lifetime_sequence int,
stripe_subscription_id text,
stripe_customer_id text,
suspicious_activity_flags int,
terms_agreed_at timestamp,
privacy_agreed_at timestamp,
last_active_at timestamp,
last_active_ip text,
temp_banned_until timestamp,
pending_deletion_at timestamp,
password_last_changed_at timestamp,
pronouns text,
acls set<text>,
deletion_reason_code int,
deletion_public_reason text,
deletion_audit_log_reason text,
first_refund_at timestamp,
PRIMARY KEY ((user_id))
);
CREATE TABLE IF NOT EXISTS fluxer.users_by_email (
email_lower text,
user_id bigint,
PRIMARY KEY ((email_lower), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.users_by_phone (
phone text,
user_id bigint,
PRIMARY KEY ((phone), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.users_by_username (
username text,
discriminator int,
user_id bigint,
PRIMARY KEY ((username), discriminator, user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.users_by_stripe_subscription_id (
stripe_subscription_id text,
user_id bigint,
PRIMARY KEY ((stripe_subscription_id), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.users_by_stripe_customer_id (
stripe_customer_id text,
user_id bigint,
PRIMARY KEY ((stripe_customer_id), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.user_activity_tracking (
activity_month text,
last_active_at timestamp,
user_id bigint,
PRIMARY KEY ((activity_month), last_active_at, user_id)
) WITH CLUSTERING ORDER BY (last_active_at ASC, user_id ASC);
CREATE TABLE IF NOT EXISTS fluxer.users_pending_deletion (
deletion_date date,
pending_deletion_at timestamp,
user_id bigint,
deletion_reason_code int,
PRIMARY KEY ((deletion_date), pending_deletion_at, user_id)
) WITH CLUSTERING ORDER BY (pending_deletion_at ASC, user_id ASC);
CREATE TYPE IF NOT EXISTS fluxer.custom_status (
text text,
emoji_id bigint,
emoji_name text,
emoji_animated boolean,
expires_at timestamp
);
CREATE TYPE IF NOT EXISTS fluxer.guild_folder (
folder_id int,
name text,
color int,
guild_ids list<bigint>
);
CREATE TABLE IF NOT EXISTS fluxer.user_settings (
user_id bigint,
locale text,
theme text,
status text,
custom_status frozen<custom_status>,
developer_mode boolean,
message_display_compact boolean,
animate_emoji boolean,
animate_stickers int,
gif_auto_play boolean,
render_embeds boolean,
render_reactions boolean,
render_spoilers int,
inline_attachment_media boolean,
inline_embed_media boolean,
explicit_content_filter int,
friend_source_flags int,
default_guilds_restricted boolean,
restricted_guilds set<bigint>,
guild_positions list<bigint>,
guild_folders frozen<list<guild_folder>>,
afk_timeout int,
time_format int,
PRIMARY KEY ((user_id))
);
CREATE TABLE IF NOT EXISTS fluxer.relationships (
source_user_id bigint,
target_user_id bigint,
type int,
nickname text,
since timestamp,
PRIMARY KEY ((source_user_id), target_user_id, type)
);
CREATE TABLE IF NOT EXISTS fluxer.notes (
source_user_id bigint,
target_user_id bigint,
note text,
PRIMARY KEY ((source_user_id), target_user_id)
);
CREATE TYPE IF NOT EXISTS fluxer.mute_config (
end_time timestamp,
selected_time_window int
);
CREATE TYPE IF NOT EXISTS fluxer.channel_override (
collapsed boolean,
message_notifications int,
muted boolean,
mute_config frozen<mute_config>
);
CREATE TABLE IF NOT EXISTS fluxer.user_guild_settings (
user_id bigint,
guild_id bigint,
message_notifications int,
muted boolean,
mute_config frozen<mute_config>,
mobile_push boolean,
suppress_everyone boolean,
suppress_roles boolean,
hide_muted_channels boolean,
channel_overrides frozen<map<bigint, channel_override>>,
version int,
PRIMARY KEY ((user_id), guild_id)
);
CREATE TABLE IF NOT EXISTS fluxer.private_channels (
user_id bigint,
channel_id bigint,
is_gdm boolean,
PRIMARY KEY ((user_id), channel_id)
);
CREATE TABLE IF NOT EXISTS fluxer.dm_states (
hi_user_id bigint,
lo_user_id bigint,
channel_id bigint,
PRIMARY KEY ((hi_user_id, lo_user_id), channel_id)
);
CREATE TABLE IF NOT EXISTS fluxer.read_states (
user_id bigint,
channel_id bigint,
message_id bigint,
mention_count int,
last_pin_timestamp timestamp,
PRIMARY KEY ((user_id), channel_id)
);
CREATE TABLE IF NOT EXISTS fluxer.recent_mentions (
user_id bigint,
message_id bigint,
channel_id bigint,
guild_id bigint,
is_everyone boolean,
is_role boolean,
PRIMARY KEY ((user_id), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC)
AND default_time_to_live = 604800;
CREATE TABLE IF NOT EXISTS fluxer.recent_mentions_by_guild (
user_id bigint,
guild_id bigint,
message_id bigint,
channel_id bigint,
is_everyone boolean,
is_role boolean,
PRIMARY KEY ((user_id, guild_id), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC)
AND default_time_to_live = 604800;
CREATE TABLE IF NOT EXISTS fluxer.saved_messages (
user_id bigint,
channel_id bigint,
message_id bigint,
saved_at timestamp,
PRIMARY KEY ((user_id), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.auth_sessions (
session_id_hash blob,
user_id bigint,
created_at timestamp,
approx_last_used_at timestamp,
client_ip text,
client_os text,
client_platform text,
client_country text,
PRIMARY KEY ((session_id_hash))
);
CREATE TABLE IF NOT EXISTS fluxer.auth_sessions_by_user_id (
user_id bigint,
session_id_hash blob,
PRIMARY KEY ((user_id), session_id_hash)
);
CREATE TABLE IF NOT EXISTS fluxer.mfa_backup_codes (
user_id bigint,
code text,
consumed boolean,
PRIMARY KEY ((user_id), code)
);
CREATE TABLE IF NOT EXISTS fluxer.webauthn_credentials (
user_id bigint,
credential_id text,
public_key blob,
counter bigint,
transports set<text>,
name text,
created_at timestamp,
last_used_at timestamp,
PRIMARY KEY (user_id, credential_id)
);
CREATE TABLE IF NOT EXISTS fluxer.webauthn_credential_lookup (
credential_id text PRIMARY KEY,
user_id bigint
);
CREATE TABLE IF NOT EXISTS fluxer.email_verification_tokens (
token_ text,
user_id bigint,
email text,
PRIMARY KEY ((token_), user_id)
) WITH default_time_to_live = 86400;
CREATE TABLE IF NOT EXISTS fluxer.password_reset_tokens (
token_ text,
user_id bigint,
email text,
PRIMARY KEY ((token_), user_id)
) WITH default_time_to_live = 3600;
CREATE TABLE IF NOT EXISTS fluxer.ip_authorization_tokens (
token_ text,
user_id bigint,
email text,
PRIMARY KEY ((token_), user_id)
) WITH default_time_to_live = 1800;
CREATE TABLE IF NOT EXISTS fluxer.email_revert_tokens (
token_ text,
user_id bigint,
email text,
PRIMARY KEY ((token_), user_id)
) WITH default_time_to_live = 172800;
CREATE TABLE IF NOT EXISTS fluxer.mfa_tickets (
ticket text,
user_id bigint,
PRIMARY KEY ((ticket), user_id)
) WITH default_time_to_live = 300;
CREATE TABLE IF NOT EXISTS fluxer.phone_tokens (
token_ text PRIMARY KEY,
phone text,
user_id bigint
);
CREATE TABLE IF NOT EXISTS fluxer.authorized_ips (
user_id bigint,
ip text,
PRIMARY KEY ((user_id, ip))
);
CREATE TABLE IF NOT EXISTS fluxer.banned_emails (
email_lower text PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS fluxer.banned_phones (
phone text PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS fluxer.banned_ips (
ip text PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS fluxer.beta_codes (
code text,
creator_id bigint,
created_at timestamp,
redeemer_id bigint,
redeemed_at timestamp,
PRIMARY KEY ((creator_id), code)
);
CREATE TABLE IF NOT EXISTS fluxer.beta_codes_by_code (
code text,
creator_id bigint,
PRIMARY KEY ((code), creator_id)
);
CREATE TABLE IF NOT EXISTS fluxer.gift_codes (
code text,
amount_cents int,
duration_months int,
created_at timestamp,
created_by_user_id bigint,
redeemed_at timestamp,
redeemed_by_user_id bigint,
stripe_payment_intent_id text,
PRIMARY KEY ((code))
);
CREATE TABLE IF NOT EXISTS fluxer.gift_codes_by_creator (
created_by_user_id bigint,
code text,
PRIMARY KEY ((created_by_user_id), code)
);
CREATE TABLE IF NOT EXISTS fluxer.gift_codes_by_redeemer (
redeemed_by_user_id bigint,
code text,
PRIMARY KEY ((redeemed_by_user_id), code)
);
CREATE TABLE IF NOT EXISTS fluxer.gift_codes_by_payment_intent (
stripe_payment_intent_id text,
code text,
PRIMARY KEY ((stripe_payment_intent_id), code)
);
CREATE TABLE IF NOT EXISTS fluxer.guilds (
guild_id bigint,
owner_id bigint,
name text,
vanity_url_code text,
icon_hash text,
banner_hash text,
splash_hash text,
features set<text>,
verification_level int,
mfa_level int,
nsfw_level int,
explicit_content_filter int,
default_message_notifications int,
system_channel_id bigint,
system_channel_flags int,
rules_channel_id bigint,
afk_channel_id bigint,
afk_timeout int,
disabled_operations int,
max_presences int,
member_count int,
audit_logs_indexed_at timestamp,
PRIMARY KEY ((guild_id))
);
CREATE TABLE IF NOT EXISTS fluxer.guilds_by_owner_id (
owner_id bigint,
guild_id bigint,
PRIMARY KEY ((guild_id), owner_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_members (
guild_id bigint,
user_id bigint,
joined_at timestamp,
nick text,
avatar_hash text,
banner_hash text,
join_source_type int,
source_invite_code text,
inviter_id bigint,
deaf boolean,
mute boolean,
communication_disabled_until timestamp,
role_ids set<bigint>,
is_premium_sanitized boolean,
bio text,
accent_color int,
pronouns text,
temporary boolean,
PRIMARY KEY ((guild_id), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_members_by_user_id (
user_id bigint,
guild_id bigint,
PRIMARY KEY ((user_id), guild_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_roles (
guild_id bigint,
role_id bigint,
name text,
permissions bigint,
position int,
color int,
icon_hash text,
unicode_emoji text,
hoist boolean,
mentionable boolean,
PRIMARY KEY ((guild_id), role_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_bans (
guild_id bigint,
user_id bigint,
moderator_id bigint,
banned_at timestamp,
expires_at timestamp,
reason text,
ip text,
PRIMARY KEY ((guild_id), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_bans_by_ip (
guild_id bigint,
ip text,
user_id bigint,
PRIMARY KEY ((guild_id, ip), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_emojis (
guild_id bigint,
emoji_id bigint,
name text,
creator_id bigint,
animated boolean,
PRIMARY KEY ((guild_id), emoji_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_emojis_by_emoji_id (
emoji_id bigint,
guild_id bigint,
name text,
creator_id bigint,
animated boolean,
PRIMARY KEY ((emoji_id))
);
CREATE TABLE IF NOT EXISTS fluxer.guild_stickers (
guild_id bigint,
sticker_id bigint,
name text,
description text,
format_type int,
tags list<text>,
creator_id bigint,
PRIMARY KEY ((guild_id), sticker_id)
);
CREATE TABLE IF NOT EXISTS fluxer.guild_stickers_by_sticker_id (
sticker_id bigint,
guild_id bigint,
name text,
description text,
format_type int,
tags list<text>,
creator_id bigint,
PRIMARY KEY ((sticker_id))
);
CREATE TABLE IF NOT EXISTS fluxer.guild_audit_logs (
guild_id bigint,
log_id bigint,
user_id bigint,
target_type text,
target_id text,
action text,
audit_log_reason text,
metadata map<text, text>,
changes text,
created_at timestamp,
PRIMARY KEY ((guild_id), log_id)
) WITH CLUSTERING ORDER BY (log_id DESC);
CREATE TYPE IF NOT EXISTS fluxer.permission_overwrite (
type int,
allow_ bigint,
deny_ bigint
);
CREATE TABLE IF NOT EXISTS fluxer.channels (
channel_id bigint,
guild_id bigint,
type int,
name text,
topic text,
icon_hash text,
url text,
parent_id bigint,
position int,
owner_id bigint,
recipient_ids set<bigint>,
nsfw boolean,
rate_limit_per_user int,
bitrate int,
user_limit int,
rtc_region text,
last_message_id bigint,
last_pin_timestamp timestamp,
permission_overwrites frozen<map<bigint, permission_overwrite>>,
nicks map<text, text>,
soft_deleted boolean,
indexed_at timestamp,
PRIMARY KEY ((channel_id, soft_deleted))
);
CREATE TABLE IF NOT EXISTS fluxer.channels_by_guild_id (
guild_id bigint,
channel_id bigint,
PRIMARY KEY ((guild_id), channel_id)
);
CREATE TABLE IF NOT EXISTS fluxer.invites (
code text,
type int,
guild_id bigint,
channel_id bigint,
inviter_id bigint,
created_at timestamp,
uses int,
max_uses int,
max_age int,
temporary boolean,
PRIMARY KEY ((code))
);
CREATE TABLE IF NOT EXISTS fluxer.invites_by_guild_id (
guild_id bigint,
code text,
PRIMARY KEY ((guild_id), code)
);
CREATE TABLE IF NOT EXISTS fluxer.invites_by_channel_id (
channel_id bigint,
code text,
PRIMARY KEY ((channel_id), code)
);
CREATE TABLE IF NOT EXISTS fluxer.webhooks (
webhook_id bigint,
webhook_token text,
type int,
guild_id bigint,
channel_id bigint,
creator_id bigint,
name text,
avatar_hash text,
PRIMARY KEY ((webhook_id), webhook_token)
);
CREATE TABLE IF NOT EXISTS fluxer.webhooks_by_guild_id (
guild_id bigint,
webhook_id bigint,
PRIMARY KEY ((guild_id), webhook_id)
);
CREATE TABLE IF NOT EXISTS fluxer.webhooks_by_channel_id (
channel_id bigint,
webhook_id bigint,
PRIMARY KEY ((channel_id), webhook_id)
);
CREATE TYPE IF NOT EXISTS fluxer.message_attachment (
attachment_id bigint,
filename text,
size bigint,
title text,
description text,
width int,
height int,
duration int,
content_type text,
content_hash text,
placeholder text,
flags int,
nsfw boolean
);
CREATE TYPE IF NOT EXISTS fluxer.message_embed_author (
name text,
url text,
icon_url text
);
CREATE TYPE IF NOT EXISTS fluxer.message_embed_provider (
name text,
url text
);
CREATE TYPE IF NOT EXISTS fluxer.message_embed_footer (
text text,
icon_url text
);
CREATE TYPE IF NOT EXISTS fluxer.message_embed_media (
url text,
width int,
height int,
duration int,
description text,
content_type text,
content_hash text,
placeholder text,
flags int
);
CREATE TYPE IF NOT EXISTS fluxer.message_embed_field (
name text,
value text,
inline boolean
);
CREATE TYPE IF NOT EXISTS fluxer.message_embed (
type text,
title text,
description text,
url text,
timestamp timestamp,
color int,
author frozen<message_embed_author>,
provider frozen<message_embed_provider>,
thumbnail frozen<message_embed_media>,
image frozen<message_embed_media>,
video frozen<message_embed_media>,
footer frozen<message_embed_footer>,
fields frozen<list<message_embed_field>>,
nsfw boolean
);
CREATE TYPE IF NOT EXISTS fluxer.message_sticker_item (
sticker_id bigint,
name text,
format_type int
);
CREATE TYPE IF NOT EXISTS fluxer.message_reference (
channel_id bigint,
message_id bigint,
guild_id bigint,
type int
);
CREATE TYPE IF NOT EXISTS fluxer.message_call (
participant_ids set<bigint>,
ended_timestamp timestamp
);
CREATE TYPE IF NOT EXISTS fluxer.message_snapshot (
content text,
timestamp timestamp,
edited_timestmap timestamp,
mention_users set<bigint>,
mention_roles set<bigint>,
mention_channels set<bigint>,
attachments frozen<list<message_attachment>>,
embeds frozen<list<message_embed>>,
sticker_items frozen<list<message_sticker_item>>,
type int,
flags int
);
CREATE TABLE IF NOT EXISTS fluxer.messages (
channel_id bigint,
bucket int,
message_id bigint,
author_id bigint,
type int,
webhook_id bigint,
webhook_name text,
webhook_avatar_hash text,
content text,
edited_timestamp timestamp,
pinned_timestamp timestamp,
flags int,
mention_everyone boolean,
mention_users set<bigint>,
mention_roles set<bigint>,
mention_channels set<bigint>,
attachments frozen<list<message_attachment>>,
embeds frozen<list<message_embed>>,
sticker_items frozen<list<message_sticker_item>>,
message_reference frozen<message_reference>,
call frozen<message_call>,
message_snapshots frozen<list<message_snapshot>>,
PRIMARY KEY ((channel_id, bucket), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.channel_pins (
channel_id bigint,
message_id bigint,
pinned_timestamp timestamp,
PRIMARY KEY ((channel_id), pinned_timestamp, message_id)
) WITH CLUSTERING ORDER BY (pinned_timestamp DESC, message_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.messages_by_author_id (
author_id bigint,
channel_id bigint,
message_id bigint,
PRIMARY KEY ((author_id), channel_id, message_id)
);
CREATE TABLE IF NOT EXISTS fluxer.message_reactions (
channel_id bigint,
bucket int,
message_id bigint,
emoji_id bigint,
emoji_name text,
user_id bigint,
emoji_animated boolean,
PRIMARY KEY ((channel_id, bucket), message_id, emoji_id, emoji_name, user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.attachment_lookup (
channel_id bigint,
attachment_id bigint,
filename text,
message_id bigint,
PRIMARY KEY ((channel_id), attachment_id, filename)
);
CREATE TABLE IF NOT EXISTS fluxer.favorite_memes (
user_id bigint,
meme_id bigint,
name text,
alt_text text,
tags list<text>,
attachment_id bigint,
filename text,
content_type text,
content_hash text,
size bigint,
width int,
height int,
duration int,
storage_key text,
created_at timestamp,
PRIMARY KEY ((user_id), meme_id)
) WITH CLUSTERING ORDER BY (meme_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.favorite_memes_by_meme_id (
meme_id bigint,
user_id bigint,
PRIMARY KEY ((meme_id), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.push_subscriptions (
user_id bigint,
subscription_id text,
endpoint text,
p256dh_key text,
auth_key text,
user_agent text,
PRIMARY KEY ((user_id), subscription_id)
);
CREATE TABLE IF NOT EXISTS fluxer.processed_payments (
payment_intent_id text,
user_id bigint,
checkout_session_id text,
price_id text,
product_type text,
amount_cents int,
currency text,
is_gift boolean,
processed_at timestamp,
PRIMARY KEY ((payment_intent_id))
);
CREATE TABLE IF NOT EXISTS fluxer.processed_payments_by_user (
user_id bigint,
payment_intent_id text,
processed_at timestamp,
PRIMARY KEY ((user_id), processed_at, payment_intent_id)
) WITH CLUSTERING ORDER BY (processed_at DESC, payment_intent_id ASC);
CREATE TABLE IF NOT EXISTS fluxer.visionary_purchases (
purchase_id bigint,
user_id bigint,
purchased_at timestamp,
sequence_number int,
stripe_payment_intent_id text,
PRIMARY KEY ((purchase_id))
);
CREATE TABLE IF NOT EXISTS fluxer.visionary_purchases_by_user (
user_id bigint,
purchase_id bigint,
PRIMARY KEY ((user_id), purchase_id)
);
CREATE TABLE IF NOT EXISTS fluxer.user_harvests (
user_id bigint,
harvest_id bigint,
requested_at timestamp,
started_at timestamp,
completed_at timestamp,
failed_at timestamp,
storage_key text,
file_size bigint,
progress_percent int,
progress_step text,
error_message text,
download_url_expires_at timestamp,
PRIMARY KEY (user_id, harvest_id)
) WITH CLUSTERING ORDER BY (harvest_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.admin_audit_logs (
log_id bigint PRIMARY KEY,
admin_user_id bigint,
target_type text,
target_id bigint,
action text,
audit_log_reason text,
metadata map<text, text>,
created_at timestamp
);
CREATE TYPE IF NOT EXISTS fluxer.iar_message_context (
message_id bigint,
author_id bigint,
author_username text,
author_discriminator int,
author_avatar_hash text,
content text,
timestamp timestamp,
edited_timestamp timestamp,
type int,
flags int,
mention_everyone boolean,
mention_users set<bigint>,
mention_roles set<bigint>,
mention_channels set<bigint>,
attachments frozen<list<message_attachment>>,
embeds frozen<list<message_embed>>,
sticker_items frozen<list<message_sticker_item>>
);
CREATE TABLE IF NOT EXISTS fluxer.iar_submissions (
report_id bigint,
reporter_id bigint,
reported_at timestamp,
status int,
report_type int,
category text,
additional_info text,
reported_user_id bigint,
reported_user_username text,
reported_user_discriminator int,
reported_user_avatar_hash text,
reported_guild_id bigint,
reported_guild_name text,
reported_guild_icon_hash text,
reported_message_id bigint,
reported_channel_id bigint,
reported_channel_name text,
message_context frozen<list<iar_message_context>>,
guild_context_id bigint,
resolved_at timestamp,
resolved_by_admin_id bigint,
public_comment text,
audit_log_reason text,
PRIMARY KEY ((report_id))
);
CREATE TABLE IF NOT EXISTS fluxer.pending_verifications (
user_id bigint,
created_at timestamp,
PRIMARY KEY ((user_id))
);
CREATE TABLE IF NOT EXISTS fluxer.pending_verifications_by_time (
created_at timestamp,
user_id bigint,
PRIMARY KEY ((created_at), user_id)
) WITH CLUSTERING ORDER BY (user_id ASC);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.gift_codes ADD visionary_sequence_number int;

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.users ADD beta_code_allowance int;
ALTER TABLE fluxer.users ADD beta_code_last_reset_at timestamp;

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.users ADD premium_will_cancel boolean;
ALTER TABLE fluxer.users ADD has_ever_purchased boolean;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.users ADD premium_billing_cycle text;

View File

@@ -0,0 +1,64 @@
CREATE TABLE IF NOT EXISTS fluxer.payments (
checkout_session_id text,
user_id bigint,
stripe_customer_id text,
payment_intent_id text,
subscription_id text,
invoice_id text,
price_id text,
product_type text,
amount_cents int,
currency text,
status text,
is_gift boolean,
gift_code text,
created_at timestamp,
completed_at timestamp,
PRIMARY KEY ((checkout_session_id))
);
CREATE TABLE IF NOT EXISTS fluxer.payments_by_payment_intent (
payment_intent_id text,
checkout_session_id text,
PRIMARY KEY ((payment_intent_id))
);
CREATE TABLE IF NOT EXISTS fluxer.payments_by_subscription (
subscription_id text,
checkout_session_id text,
user_id bigint,
price_id text,
product_type text,
PRIMARY KEY ((subscription_id))
);
CREATE TABLE IF NOT EXISTS fluxer.payments_by_user (
user_id bigint,
created_at timestamp,
checkout_session_id text,
PRIMARY KEY ((user_id), created_at)
) WITH CLUSTERING ORDER BY (created_at DESC);
ALTER TABLE fluxer.gift_codes ADD checkout_session_id text;
CREATE TABLE IF NOT EXISTS fluxer.visionary_users (
sequence_number int,
user_id bigint,
checkout_session_id text,
gift_code text,
granted_at timestamp,
PRIMARY KEY ((sequence_number))
);
CREATE TABLE IF NOT EXISTS fluxer.visionary_users_by_user (
user_id bigint,
sequence_number int,
granted_at timestamp,
PRIMARY KEY ((user_id))
);
CREATE TABLE IF NOT EXISTS fluxer.visionary_counter (
id text,
next_sequence_number counter,
PRIMARY KEY ((id))
);

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.users ADD gift_inventory_server_seq int;
ALTER TABLE fluxer.users ADD gift_inventory_client_seq int;

View File

@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS fluxer.visionary_slots (
slot_index int,
user_id bigint,
PRIMARY KEY ((slot_index))
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.favorite_memes ADD is_gifv boolean;

View File

@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS fluxer.pinned_dms (
user_id bigint,
channel_id bigint,
sort_order int,
PRIMARY KEY ((user_id), channel_id)
);

View File

@@ -0,0 +1,32 @@
CREATE TABLE IF NOT EXISTS fluxer.voice_regions (
id text PRIMARY KEY,
name text,
emoji text,
latitude double,
longitude double,
priority int,
is_default boolean,
vip_only boolean,
required_guild_features set<text>,
allowed_guild_ids set<bigint>,
allowed_user_ids set<bigint>,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE IF NOT EXISTS fluxer.voice_servers (
region_id text,
server_id text,
endpoint text,
api_key text,
api_secret text,
priority int,
is_active boolean,
vip_only boolean,
required_guild_features set<text>,
allowed_guild_ids set<bigint>,
allowed_user_ids set<bigint>,
created_at timestamp,
updated_at timestamp,
PRIMARY KEY (region_id, server_id)
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.user_settings ADD incoming_call_flags int;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.user_settings ADD group_dm_add_permission_flags int;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.favorite_memes ADD tenor_id bigint;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.users ADD global_name text;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.users ADD premium_onboarding_dismissed_at timestamp;

View File

@@ -0,0 +1,135 @@
CREATE TABLE IF NOT EXISTS fluxer.oauth_clients (
client_id bigint PRIMARY KEY,
client_secret_hash text,
name text,
description text,
icon_url text,
owner_user_id bigint,
team_id bigint,
type text,
redirect_uris set<text>,
scopes set<text>,
grant_types set<text>,
homepage_url text,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_clients_by_owner (
owner_user_id bigint,
client_id bigint,
PRIMARY KEY ((owner_user_id), client_id)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_authorization_requests (
request_id text PRIMARY KEY,
client_id bigint,
redirect_uri text,
scope set<text>,
state text,
code_challenge text,
code_challenge_method text,
nonce text,
created_at timestamp,
expires_at timestamp
) WITH default_time_to_live = 900;
CREATE TABLE IF NOT EXISTS fluxer.oauth_authorization_codes (
code text PRIMARY KEY,
client_id bigint,
user_id bigint,
redirect_uri text,
scope set<text>,
code_challenge text,
code_challenge_method text,
nonce text,
created_at timestamp,
expires_at timestamp
) WITH default_time_to_live = 900;
CREATE TABLE IF NOT EXISTS fluxer.oauth_access_tokens (
token_ text PRIMARY KEY,
client_id bigint,
user_id bigint,
scope set<text>,
created_at timestamp,
expires_at timestamp
) WITH default_time_to_live = 86400;
CREATE TABLE IF NOT EXISTS fluxer.oauth_access_tokens_by_client (
client_id bigint,
token_ text,
PRIMARY KEY ((client_id), token_)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_access_tokens_by_user (
user_id bigint,
token_ text,
PRIMARY KEY ((user_id), token_)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_refresh_tokens (
token_ text PRIMARY KEY,
client_id bigint,
user_id bigint,
scope set<text>,
created_at timestamp,
expires_at timestamp
) WITH default_time_to_live = 2592000;
CREATE TABLE IF NOT EXISTS fluxer.oauth_refresh_tokens_by_client (
client_id bigint,
token_ text,
PRIMARY KEY ((client_id), token_)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_refresh_tokens_by_user (
user_id bigint,
token_ text,
PRIMARY KEY ((user_id), token_)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_teams (
team_id bigint PRIMARY KEY,
name text,
owner_user_id bigint,
created_at timestamp
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_teams_by_owner (
owner_user_id bigint,
team_id bigint,
PRIMARY KEY ((owner_user_id), team_id)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_team_members (
team_id bigint,
user_id bigint,
role text,
added_at timestamp,
PRIMARY KEY ((team_id), user_id)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_team_members_by_user (
user_id bigint,
team_id bigint,
PRIMARY KEY ((user_id), team_id)
);
CREATE TABLE IF NOT EXISTS fluxer.oauth_bot_tokens (
token_ text PRIMARY KEY,
client_id bigint,
user_id bigint,
scopes set<text>,
created_at timestamp,
revoked boolean
);
CREATE TABLE IF NOT EXISTS fluxer.oidc_keys (
kid text PRIMARY KEY,
alg text,
public_jwk text,
private_jwk text,
created_at timestamp,
active boolean
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.favorite_memes ADD tenor_id_str text;

View File

@@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS fluxer.oidc_keys_by_kid (
kid text PRIMARY KEY,
alg text,
public_jwk text,
private_jwk text,
created_at timestamp
);
CREATE TABLE IF NOT EXISTS fluxer.oidc_key_status (
name text PRIMARY KEY,
active_kid text,
published_kids set<text>,
updated_at timestamp
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.guild_members ADD profile_flags int;

View File

@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS fluxer.push_devices (
user_id bigint,
device_id text,
fcm_token text,
platform text,
device_name text,
created_at timestamp,
updated_at timestamp,
PRIMARY KEY (user_id, device_id)
);

View File

@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS fluxer.push_devices_by_fcm_token (
fcm_token text,
user_id bigint,
device_id text,
PRIMARY KEY ((fcm_token), user_id, device_id)
);

View File

@@ -0,0 +1,7 @@
ALTER TABLE fluxer.push_devices WITH default_time_to_live = 7776000;
ALTER TABLE fluxer.push_devices_by_fcm_token WITH default_time_to_live = 7776000;
ALTER TABLE fluxer.push_subscriptions WITH default_time_to_live = 7776000;
ALTER TABLE fluxer.phone_tokens WITH default_time_to_live = 2592000;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.oauth_clients ADD bot_is_public boolean;

View File

@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS fluxer.oauth_bot_tokens_by_client (
client_id bigint,
token_ text,
user_id bigint,
scopes set<text>,
created_at timestamp,
PRIMARY KEY (client_id, token_)
);

View File

@@ -0,0 +1,63 @@
CREATE TABLE IF NOT EXISTS fluxer.applications (
application_id bigint PRIMARY KEY,
owner_user_id bigint,
name text,
bot_user_id bigint,
is_confidential boolean,
oauth2_redirect_uris set<text>,
oauth2_scopes set<text>,
client_secret_hash text,
bot_token_hash text,
bot_token_preview text,
bot_token_created_at timestamp,
client_secret_created_at timestamp
);
CREATE TABLE IF NOT EXISTS fluxer.applications_by_owner (
owner_user_id bigint,
application_id bigint,
PRIMARY KEY ((owner_user_id), application_id)
) WITH CLUSTERING ORDER BY (application_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.oauth2_authorization_codes (
code text PRIMARY KEY,
application_id bigint,
user_id bigint,
redirect_uri text,
scope set<text>,
code_challenge text,
code_challenge_method text,
nonce text,
created_at timestamp,
expires_at timestamp
) WITH default_time_to_live = 600;
CREATE TABLE IF NOT EXISTS fluxer.oauth2_access_tokens (
token_ text PRIMARY KEY,
application_id bigint,
user_id bigint,
scope set<text>,
created_at timestamp,
expires_at timestamp
) WITH default_time_to_live = 3600;
CREATE TABLE IF NOT EXISTS fluxer.oauth2_access_tokens_by_user (
user_id bigint,
token_ text,
PRIMARY KEY ((user_id), token_)
) WITH CLUSTERING ORDER BY (token_ DESC);
CREATE TABLE IF NOT EXISTS fluxer.oauth2_refresh_tokens (
token_ text PRIMARY KEY,
application_id bigint,
user_id bigint,
scope set<text>,
created_at timestamp,
expires_at timestamp
) WITH default_time_to_live = 2592000;
CREATE TABLE IF NOT EXISTS fluxer.oauth2_refresh_tokens_by_user (
user_id bigint,
token_ text,
PRIMARY KEY ((user_id), token_)
) WITH CLUSTERING ORDER BY (token_ DESC);

View File

@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS fluxer.authorized_ips_v2 (
user_id bigint,
ip text,
PRIMARY KEY (user_id, ip)
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.guilds ADD banner_height int;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.guilds ADD banner_width int;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.applications ADD bot_is_public boolean;

View File

@@ -0,0 +1,56 @@
CREATE TABLE IF NOT EXISTS fluxer.admin_archives_by_subject (
subject_type text,
subject_id bigint,
archive_id bigint,
requested_by bigint,
requested_at timestamp,
started_at timestamp,
completed_at timestamp,
failed_at timestamp,
storage_key text,
file_size bigint,
progress_percent int,
progress_step text,
error_message text,
download_url_expires_at timestamp,
expires_at timestamp,
PRIMARY KEY ((subject_type, subject_id), archive_id)
) WITH CLUSTERING ORDER BY (archive_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.admin_archives_by_requester (
requested_by bigint,
archive_id bigint,
subject_type text,
subject_id bigint,
requested_at timestamp,
started_at timestamp,
completed_at timestamp,
failed_at timestamp,
storage_key text,
file_size bigint,
progress_percent int,
progress_step text,
error_message text,
download_url_expires_at timestamp,
expires_at timestamp,
PRIMARY KEY ((requested_by), archive_id)
) WITH CLUSTERING ORDER BY (archive_id DESC);
CREATE TABLE IF NOT EXISTS fluxer.admin_archives_by_type (
subject_type text,
archive_id bigint,
subject_id bigint,
requested_by bigint,
requested_at timestamp,
started_at timestamp,
completed_at timestamp,
failed_at timestamp,
storage_key text,
file_size bigint,
progress_percent int,
progress_step text,
error_message text,
download_url_expires_at timestamp,
expires_at timestamp,
PRIMARY KEY ((subject_type), archive_id)
) WITH CLUSTERING ORDER BY (archive_id DESC);

View File

@@ -0,0 +1,5 @@
ALTER TABLE fluxer.oauth2_access_tokens
WITH default_time_to_live = 604800;
ALTER TABLE fluxer.oauth2_access_tokens_by_user
WITH default_time_to_live = 604800;

View File

@@ -0,0 +1,22 @@
CREATE TABLE IF NOT EXISTS fluxer.attachment_decay_by_id (
attachment_id bigint PRIMARY KEY,
channel_id bigint,
message_id bigint,
filename text,
size_bytes varint,
uploaded_at timestamp,
expires_at timestamp,
last_accessed_at timestamp,
cost double,
lifetime_days int,
status text
);
CREATE TABLE IF NOT EXISTS fluxer.attachment_decay_by_expiry (
expiry_bucket int,
expires_at timestamp,
attachment_id bigint,
channel_id bigint,
message_id bigint,
PRIMARY KEY ((expiry_bucket), expires_at, attachment_id)
);

View File

@@ -0,0 +1,25 @@
CREATE TABLE IF NOT EXISTS fluxer.email_change_tickets (
ticket text PRIMARY KEY,
user_id bigint,
require_original boolean,
original_email text,
original_verified boolean,
original_code text,
original_code_sent_at timestamp,
original_code_expires_at timestamp,
new_email text,
new_code text,
new_code_sent_at timestamp,
new_code_expires_at timestamp,
status text,
created_at timestamp,
updated_at timestamp
);
CREATE TABLE IF NOT EXISTS fluxer.email_change_tokens (
token_ text PRIMARY KEY,
user_id bigint,
new_email text,
expires_at timestamp,
created_at timestamp
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.email_change_tickets ADD original_proof text;

View File

@@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS fluxer.user_contact_change_logs (
user_id bigint,
event_id timeuuid,
field text,
old_value text,
new_value text,
reason text,
actor_user_id bigint,
event_at timestamp,
PRIMARY KEY ((user_id), event_id)
) WITH CLUSTERING ORDER BY (event_id DESC);

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.users ADD avatar_color int;
ALTER TABLE fluxer.users ADD banner_color int;

View File

@@ -0,0 +1,51 @@
CREATE TABLE IF NOT EXISTS fluxer.guild_audit_logs_v2 (
guild_id bigint,
log_id bigint,
user_id bigint,
target_id text,
action_type int,
reason text,
options map<text, text>,
changes text,
PRIMARY KEY ((guild_id), log_id)
) WITH CLUSTERING ORDER BY (log_id DESC)
AND default_time_to_live = 3888000;
CREATE TABLE IF NOT EXISTS fluxer.guild_audit_logs_v2_by_user (
guild_id bigint,
user_id bigint,
log_id bigint,
target_id text,
action_type int,
reason text,
options map<text, text>,
changes text,
PRIMARY KEY ((guild_id, user_id), log_id)
) WITH CLUSTERING ORDER BY (log_id DESC)
AND default_time_to_live = 3888000;
CREATE TABLE IF NOT EXISTS fluxer.guild_audit_logs_v2_by_action (
guild_id bigint,
action_type int,
log_id bigint,
user_id bigint,
target_id text,
reason text,
options map<text, text>,
changes text,
PRIMARY KEY ((guild_id, action_type), log_id)
) WITH CLUSTERING ORDER BY (log_id DESC)
AND default_time_to_live = 3888000;
CREATE TABLE IF NOT EXISTS fluxer.guild_audit_logs_v2_by_user_action (
guild_id bigint,
user_id bigint,
action_type int,
log_id bigint,
target_id text,
reason text,
options map<text, text>,
changes text,
PRIMARY KEY ((guild_id, user_id, action_type), log_id)
) WITH CLUSTERING ORDER BY (log_id DESC)
AND default_time_to_live = 3888000;

View File

@@ -0,0 +1,3 @@
ALTER TABLE fluxer.users ADD pending_bulk_message_deletion_at timestamp;
ALTER TABLE fluxer.users ADD pending_bulk_message_deletion_channel_count int;
ALTER TABLE fluxer.users ADD pending_bulk_message_deletion_message_count int;

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.guilds ADD splash_width int;
ALTER TABLE fluxer.guilds ADD splash_height int;

View File

@@ -0,0 +1,3 @@
ALTER TABLE fluxer.guilds ADD embed_splash_hash text;
ALTER TABLE fluxer.guilds ADD embed_splash_width int;
ALTER TABLE fluxer.guilds ADD embed_splash_height int;

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS fluxer.channel_state (
channel_id bigint PRIMARY KEY,
created_bucket int,
has_messages boolean,
last_message_id bigint,
last_message_bucket int,
updated_at timestamp
);
CREATE TABLE IF NOT EXISTS fluxer.channel_message_buckets (
channel_id bigint,
bucket int,
updated_at timestamp,
PRIMARY KEY (channel_id, bucket)
) WITH CLUSTERING ORDER BY (bucket DESC);

View File

@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS fluxer.channel_empty_buckets (
channel_id bigint,
bucket int,
updated_at timestamp,
PRIMARY KEY ((channel_id), bucket)
) WITH CLUSTERING ORDER BY (bucket DESC);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.payments ADD version int;

View File

@@ -0,0 +1,20 @@
ALTER TABLE fluxer.users ADD version int;
ALTER TABLE fluxer.user_settings ADD version int;
ALTER TABLE fluxer.guilds ADD version int;
ALTER TABLE fluxer.guild_members ADD version int;
ALTER TABLE fluxer.guild_roles ADD version int;
ALTER TABLE fluxer.guild_emojis ADD version int;
ALTER TABLE fluxer.guild_stickers ADD version int;
ALTER TABLE fluxer.channels ADD version int;
ALTER TABLE fluxer.messages ADD version int;
ALTER TABLE fluxer.notes ADD version int;
ALTER TABLE fluxer.relationships ADD version int;
ALTER TABLE fluxer.favorite_memes ADD version int;
ALTER TABLE fluxer.invites ADD version int;
ALTER TABLE fluxer.webhooks ADD version int;
ALTER TABLE fluxer.applications ADD version int;
ALTER TABLE fluxer.auth_sessions ADD version int;
ALTER TABLE fluxer.gift_codes ADD version int;
ALTER TABLE fluxer.beta_codes ADD version int;
ALTER TABLE fluxer.pending_verifications ADD version int;
ALTER TABLE fluxer.webauthn_credentials ADD version int;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.guilds ADD splash_card_alignment int;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.messages ADD has_reaction boolean;

View File

@@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS fluxer.instance_configuration (
key text PRIMARY KEY,
value text,
updated_at timestamp
);
INSERT INTO fluxer.instance_configuration (key, value, updated_at)
VALUES ('manual_review_enabled', 'true', toTimestamp(now()));
INSERT INTO fluxer.instance_configuration (key, value, updated_at)
VALUES ('manual_review_schedule_enabled', 'false', toTimestamp(now()));
INSERT INTO fluxer.instance_configuration (key, value, updated_at)
VALUES ('manual_review_schedule_start_hour_utc', '0', toTimestamp(now()));
INSERT INTO fluxer.instance_configuration (key, value, updated_at)
VALUES ('manual_review_schedule_end_hour_utc', '23', toTimestamp(now()));

View File

@@ -0,0 +1,18 @@
CREATE TABLE IF NOT EXISTS fluxer.expression_packs (
pack_id bigint PRIMARY KEY,
pack_type text,
creator_id bigint,
name text,
description text,
created_at timestamp,
updated_at timestamp,
version int
);
CREATE TABLE IF NOT EXISTS fluxer.pack_installations (
user_id bigint,
pack_id bigint,
pack_type text,
installed_at timestamp,
PRIMARY KEY (user_id, pack_id)
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.pending_verifications ADD metadata map<text, text>;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.auth_sessions ADD client_location text;

View File

@@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS fluxer.scheduled_messages (
user_id bigint,
scheduled_message_id bigint,
channel_id bigint,
payload text,
scheduled_at timestamp,
scheduled_local_at text,
timezone text,
status text,
status_reason text,
created_at timestamp,
invalidated_at timestamp,
PRIMARY KEY (user_id, scheduled_message_id)
) WITH CLUSTERING ORDER BY (scheduled_message_id DESC);

View File

@@ -0,0 +1,20 @@
ALTER TABLE fluxer.iar_submissions ADD reporter_email text;
ALTER TABLE fluxer.iar_submissions ADD reporter_full_legal_name text;
ALTER TABLE fluxer.iar_submissions ADD reporter_country_of_residence text;
ALTER TABLE fluxer.iar_submissions ADD reported_guild_invite_code text;
CREATE TABLE IF NOT EXISTS fluxer.dsa_report_email_verifications (
email_lower text,
code_hash text,
expires_at timestamp,
last_sent_at timestamp,
PRIMARY KEY ((email_lower))
);
CREATE TABLE IF NOT EXISTS fluxer.dsa_report_tickets (
ticket text,
email_lower text,
expires_at timestamp,
created_at timestamp,
PRIMARY KEY ((ticket))
);

View File

@@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS fluxer.expression_packs_by_creator (
creator_id bigint,
pack_id bigint,
pack_type text,
name text,
description text,
created_at timestamp,
updated_at timestamp,
version int,
PRIMARY KEY (creator_id, pack_id)
);

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.user_settings ADD status_resets_at timestamp;
ALTER TABLE fluxer.user_settings ADD status_resets_to text;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.guild_roles ADD hoist_position int;

View File

@@ -0,0 +1,28 @@
CREATE TABLE IF NOT EXISTS fluxer.swish_payments (
payment_id text,
user_id bigint,
product_type text,
status text,
is_gift boolean,
amount int,
currency text,
message text,
payer_alias text,
payment_reference text,
gift_code text,
created_at timestamp,
completed_at timestamp,
PRIMARY KEY ((payment_id))
);
CREATE TABLE IF NOT EXISTS fluxer.swish_payments_by_user (
user_id bigint,
payment_id text,
product_type text,
status text,
is_gift boolean,
amount int,
currency text,
created_at timestamp,
PRIMARY KEY ((user_id), created_at)
) WITH CLUSTERING ORDER BY (created_at DESC);

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.auth_sessions ADD client_user_agent text;
ALTER TABLE fluxer.auth_sessions ADD client_is_desktop boolean;

View File

@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS fluxer.user_dm_history (
user_id bigint,
channel_id bigint,
PRIMARY KEY ((user_id), channel_id)
);

View File

@@ -0,0 +1 @@
ALTER TYPE fluxer.message_snapshot ADD edited_timestamp timestamp;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.users ADD traits set<text>;

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.user_settings ADD bot_default_guilds_restricted boolean;
ALTER TABLE fluxer.user_settings ADD bot_restricted_guilds set<bigint>;

View File

@@ -0,0 +1,20 @@
CREATE TABLE IF NOT EXISTS fluxer.system_dm_jobs (
job_type text,
job_id bigint,
admin_user_id bigint,
status text,
content text,
registration_start timestamp,
registration_end timestamp,
excluded_guild_ids set<text>,
target_count int,
sent_count int,
failed_count int,
last_error text,
worker_job_key text,
created_at timestamp,
updated_at timestamp,
approved_by bigint,
approved_at timestamp,
PRIMARY KEY ((job_type), job_id)
) WITH CLUSTERING ORDER BY (job_id DESC);

View File

@@ -0,0 +1,22 @@
CREATE TABLE IF NOT EXISTS fluxer.admin_api_keys (
key_id bigint,
key_hash text,
name text,
created_by_user_id bigint,
created_at timestamp,
last_used_at timestamp,
expires_at timestamp,
version int,
PRIMARY KEY (key_id)
);
CREATE TABLE IF NOT EXISTS fluxer.admin_api_keys_by_creator (
created_by_user_id bigint,
key_id bigint,
created_at timestamp,
name text,
expires_at timestamp,
last_used_at timestamp,
version int,
PRIMARY KEY (created_by_user_id, key_id)
) WITH CLUSTERING ORDER BY (key_id DESC);

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.admin_api_keys ADD acls set<text>;
ALTER TABLE fluxer.admin_api_keys_by_creator ADD acls set<text>;

View File

@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS fluxer.relationships_by_target (
target_user_id bigint,
source_user_id bigint,
type int,
nickname text,
since timestamp,
version int,
PRIMARY KEY (target_user_id, source_user_id, type)
) WITH CLUSTERING ORDER BY (source_user_id ASC, type ASC);

View File

@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS fluxer.messages_by_author_id_v2 (
author_id bigint,
message_id bigint,
channel_id bigint,
PRIMARY KEY ((author_id), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

View File

@@ -0,0 +1,28 @@
CREATE TABLE IF NOT EXISTS fluxer.csam_evidence_packages (
report_id bigint PRIMARY KEY,
resource_type text,
bucket text,
key text,
cdn_url text,
filename text,
content_type text,
channel_id bigint,
message_id bigint,
guild_id bigint,
user_id bigint,
match_tracking_id text,
match_details text,
frames text,
hashes text,
context_snapshot text,
created_at timestamp,
expires_at timestamp,
integrity_sha256 text,
evidence_zip_key text
);
CREATE TABLE IF NOT EXISTS fluxer.csam_evidence_legal_holds (
report_id bigint PRIMARY KEY,
held_until timestamp,
created_at timestamp
);

View File

@@ -0,0 +1,21 @@
CREATE TABLE IF NOT EXISTS fluxer.csam_scan_jobs (
job_id text PRIMARY KEY,
resource_type text,
bucket text,
key text,
cdn_url text,
filename text,
content_type text,
channel_id bigint,
message_id bigint,
guild_id bigint,
user_id bigint,
status text,
enqueue_time timestamp,
last_updated timestamp,
match_tracking_id text,
match_details text,
hashes text,
error_message text,
expires_at timestamp
);

View File

@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS fluxer.csam_evidence_expirations (
bucket text,
expires_at timestamp,
report_id bigint,
PRIMARY KEY ((bucket), expires_at, report_id)
) WITH CLUSTERING ORDER BY (expires_at ASC);

View File

@@ -0,0 +1,2 @@
ALTER TABLE fluxer.guild_stickers ADD animated boolean;
ALTER TABLE fluxer.guild_stickers_by_sticker_id ADD animated boolean;

View File

@@ -0,0 +1 @@
ALTER TYPE fluxer.message_sticker_item ADD animated boolean;

View File

@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS fluxer.ncmec_submissions (
report_id bigint PRIMARY KEY,
status text,
ncmec_report_id text,
submitted_at timestamp,
submitted_by_admin_id bigint,
failure_reason text,
created_at timestamp,
updated_at timestamp
);

View File

@@ -0,0 +1,2 @@
ALTER TYPE fluxer.message_attachment ADD duration_secs int;
ALTER TYPE fluxer.message_attachment ADD waveform text;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.favorite_memes ADD klipy_slug text;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.user_settings ADD trusted_domains set<text>;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.user_settings ADD default_hide_muted_channels boolean;

View File

@@ -0,0 +1,2 @@
ALTER TYPE fluxer.guild_folder ADD flags int;
ALTER TYPE fluxer.guild_folder ADD icon text;

View File

@@ -0,0 +1,17 @@
CREATE TYPE IF NOT EXISTS fluxer.message_embed_child (
type text,
title text,
description text,
url text,
timestamp timestamp,
color int,
author frozen<message_embed_author>,
provider frozen<message_embed_provider>,
thumbnail frozen<message_embed_media>,
image frozen<message_embed_media>,
video frozen<message_embed_media>,
footer frozen<message_embed_footer>,
fields frozen<list<message_embed_field>>,
nsfw boolean
);
ALTER TYPE fluxer.message_embed ADD children frozen<list<message_embed_child>>;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.guilds ADD message_history_cutoff timestamp;

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.guilds ADD members_indexed_at timestamp;

View File

@@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS fluxer.user_connections (
user_id bigint,
connection_type text,
connection_id text,
identifier text,
name text,
verified boolean,
visibility_flags int,
sort_order int,
verification_token text,
verified_at timestamp,
last_verified_at timestamp,
created_at timestamp,
version int,
PRIMARY KEY ((user_id), connection_type, connection_id)
) WITH CLUSTERING ORDER BY (connection_type ASC, connection_id DESC);

View File

@@ -0,0 +1,46 @@
CREATE TABLE IF NOT EXISTS fluxer.donors (
email text,
stripe_customer_id text,
business_name text,
tax_id text,
tax_id_type text,
stripe_subscription_id text,
subscription_amount_cents int,
subscription_currency text,
subscription_interval text,
subscription_current_period_end timestamp,
subscription_cancel_at timestamp,
created_at timestamp,
updated_at timestamp,
version int,
PRIMARY KEY ((email))
);
CREATE TABLE IF NOT EXISTS fluxer.donors_by_stripe_customer_id (
stripe_customer_id text,
email text,
PRIMARY KEY ((stripe_customer_id), email)
);
CREATE TABLE IF NOT EXISTS fluxer.donors_by_stripe_subscription_id (
stripe_subscription_id text,
email text,
PRIMARY KEY ((stripe_subscription_id), email)
);
CREATE TABLE IF NOT EXISTS fluxer.donor_magic_link_tokens (
token_ text,
donor_email text,
expires_at timestamp,
used_at timestamp,
PRIMARY KEY ((token_))
) WITH default_time_to_live = 900;
CREATE TABLE IF NOT EXISTS fluxer.donor_magic_link_tokens_by_email (
donor_email text,
token_ text,
PRIMARY KEY ((donor_email), token_)
) WITH default_time_to_live = 900;

View File

@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS fluxer.password_change_tickets (
ticket text PRIMARY KEY,
user_id bigint,
code text,
code_sent_at timestamp,
code_expires_at timestamp,
verified boolean,
verification_proof text,
status text,
created_at timestamp,
updated_at timestamp
);

View File

@@ -0,0 +1 @@
ALTER TABLE fluxer.applications ADD bot_require_code_grant boolean;

View File

@@ -0,0 +1,21 @@
CREATE TABLE IF NOT EXISTS fluxer.guild_discovery (
guild_id bigint,
status text,
category_type int,
description text,
applied_at timestamp,
reviewed_at timestamp,
reviewed_by bigint,
review_reason text,
removed_at timestamp,
removed_by bigint,
removal_reason text,
PRIMARY KEY ((guild_id))
);
CREATE TABLE IF NOT EXISTS fluxer.guild_discovery_by_status (
status text,
applied_at timestamp,
guild_id bigint,
PRIMARY KEY ((status), applied_at, guild_id)
) WITH CLUSTERING ORDER BY (applied_at DESC, guild_id DESC);

View File

@@ -0,0 +1,38 @@
services:
clamav:
image: clamav/clamav:1.4
hostname: clamav
volumes:
- clamav_data:/var/lib/clamav
- ./conf/clamd.conf:/etc/clamav/clamd.conf:ro
networks:
- fluxer-shared
ports:
- '3310:3310'
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
resources:
limits:
cpus: '2'
memory: 4G
reservations:
cpus: '1'
memory: 2G
healthcheck:
test: ['CMD-SHELL', 'clamdscan --version || exit 1']
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
networks:
fluxer-shared:
external: true
volumes:
clamav_data:
driver: local

View File

@@ -0,0 +1,54 @@
# Listening
LocalSocket /tmp/clamd.sock
TCPSocket 3310
TCPAddr 0.0.0.0
# Threading
MaxThreads 12
MaxConnectionQueueLength 30
# Scanner limits
MaxScanSize 150M
MaxFileSize 100M
MaxRecursion 16
MaxFiles 10000
MaxEmbeddedPE 10M
MaxHTMLNormalize 10M
MaxHTMLNoTags 2M
MaxScriptNormalize 5M
MaxZipTypeRcg 1M
# Scanning options
ScanPE yes
ScanELF yes
ScanOLE2 yes
ScanPDF yes
ScanSWF yes
ScanHTML yes
ScanMail yes
ScanArchive yes
ScanPartialMessages yes
AlertBrokenExecutables yes
AlertEncrypted no
AlertEncryptedArchive no
AlertEncryptedDoc no
AlertOLE2Macros yes
AlertPhishingSSLMismatch no
AlertPhishingCloak no
# Database
DatabaseDirectory /var/lib/clamav
OfficialDatabaseOnly no
DetectPUA yes
ExcludePUA NetTool
ExcludePUA PWTool
HeuristicScanPrecedence yes
# Logging
LogTime yes
LogClean no
LogVerbose no
ExtendedDetectionInfo yes
# Performance
BytecodeTimeout 60000

View File

@@ -0,0 +1,86 @@
services:
ghost-blog-mysql:
image: mysql:8.0
hostname: ghost-blog-mysql
env_file:
- /etc/fluxer/ghost-blog.env
environment:
- MYSQL_DATABASE=ghost
volumes:
- ghost_blog_mysql:/var/lib/mysql
networks:
- fluxer-shared
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
resources:
limits:
cpus: '2'
memory: 2G
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
ghost-blog:
image: ghost:5-alpine
hostname: ghost-blog
env_file:
- /etc/fluxer/ghost-blog.env
environment:
- url=https://blog.fluxer.app
- database__client=mysql
- database__connection__host=ghost-blog-mysql
- database__connection__database=ghost
- database__pool__min=0
- database__pool__acquireTimeoutMillis=60000
- database__connection__connectTimeout=60000
volumes:
- ghost_blog_content:/var/lib/ghost/content
networks:
- fluxer-shared
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
resources:
limits:
cpus: '2'
memory: 1G
labels:
- 'caddy=blog.fluxer.app'
- 'caddy.reverse_proxy={{upstreams 2368}}'
- 'caddy.header.Strict-Transport-Security="max-age=31536000; includeSubDomains; preload"'
- 'caddy.header.X-Xss-Protection="1; mode=block"'
- 'caddy.header.X-Content-Type-Options=nosniff'
- 'caddy.header.Referrer-Policy=strict-origin-when-cross-origin'
- 'caddy.header.X-Frame-Options=SAMEORIGIN'
healthcheck:
test:
[
'CMD',
'node',
'-e',
"require('http').get('http://127.0.0.1:2368/', r => process.exit(r.statusCode < 400 ? 0 : 1)).on('error', () => process.exit(1))",
]
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
networks:
fluxer-shared:
external: true
volumes:
ghost_blog_mysql:
driver: local
ghost_blog_content:
driver: local

View File

@@ -0,0 +1,172 @@
# livekitctl
A CLI tool for bootstrapping self-hosted LiveKit SFU infrastructure for Fluxer voice and video.
## Installation
```bash
curl -fsSL https://fluxer.app/get/livekitctl | sudo bash
```
## Overview
livekitctl automates the installation and configuration of a complete LiveKit media server stack including:
- **LiveKit** - WebRTC SFU for voice and video
- **Caddy** - Reverse proxy with automatic TLS (built with L4 module for TCP/UDP)
- **coturn** - TURN/STUN server for NAT traversal
- **KV store** - Redis-compatible key-value store for LiveKit state
## Prerequisites
- Linux server (Debian/Ubuntu, RHEL/CentOS, or Arch-based)
- Root access
- DNS records configured for your LiveKit and TURN domains pointing to your server's public IP
## Commands
### bootstrap
Install and configure the complete LiveKit stack.
```bash
livekitctl bootstrap \
--livekit-domain livekit.example.com \
--turn-domain turn.example.com \
--email admin@example.com
```
Required flags:
- `--livekit-domain <domain>` - Domain for LiveKit WebSocket/HTTP connections
- `--turn-domain <domain>` - Domain for TURN relay server
- `--email <email>` - ACME email for TLS certificate issuance
Optional flags:
- `--livekit-version <version>` - LiveKit version (default: v1.9.11)
- `--caddy-version <version>` - Caddy version (default: v2.10.2)
- `--caddy-l4-version <version>` - Caddy L4 module version (default: master)
- `--xcaddy-version <version>` - xcaddy build tool version (default: v0.4.5)
- `--install-dir <path>` - Override LiveKit install directory (default: /opt/livekit)
- `--firewall` - Configure detected firewall tool (ufw, firewalld, iptables)
- `--kv-port <port>` - KV store port (default: 6379)
- `--kv-port-auto` - Pick a free KV port from 6379-6382
- `--webhook-url <url>` - Webhook URL (repeatable)
- `--webhook-urls-file <file>` - File with webhook URLs (one per line)
- `--allow-http-webhooks` - Allow http:// webhook URLs
- `--dns-timeout <seconds>` - DNS wait timeout (default: 900)
- `--dns-interval <seconds>` - DNS check interval (default: 10)
- `--print-secrets` - Print generated secrets JSON to stdout
### status
Show systemd service status for all managed services.
```bash
livekitctl status
```
### logs
Show systemd logs for a specific service.
```bash
livekitctl logs --service livekit.service [--lines 200]
```
Flags:
- `--service <unit>` - systemd unit name (required), e.g., `livekit.service`, `caddy.service`
- `--lines <n>` - Number of log lines to show (default: 200)
### restart
Restart one or more services. If no services specified, restarts all managed services.
```bash
livekitctl restart [services...]
```
Examples:
```bash
livekitctl restart # Restart all services
livekitctl restart livekit.service # Restart only LiveKit
livekitctl restart caddy.service livekit-coturn.service
```
Managed services:
- `livekit-kv.service` - KV store
- `livekit-coturn.service` - TURN server
- `livekit.service` - LiveKit SFU
- `caddy.service` - Reverse proxy
### webhook
Manage LiveKit webhook URLs. Changes are written to config and LiveKit is restarted.
```bash
livekitctl webhook list
livekitctl webhook add <url> [--allow-http-webhooks]
livekitctl webhook remove <url>
livekitctl webhook set --url <url> [--url <url>...] [--file <path>] [--allow-http-webhooks]
```
Subcommands:
- `list` - List configured webhook URLs
- `add <url>` - Add a webhook URL
- `remove <url>` - Remove a webhook URL
- `set` - Replace all webhook URLs
## Port configuration
Default port allocations:
| Port | Protocol | Service |
| ----------- | -------- | ------------------------- |
| 7880 | TCP | LiveKit HTTP (internal) |
| 7881 | TCP | LiveKit RTC |
| 50000-60000 | UDP | LiveKit RTC media |
| 3478 | UDP | TURN listen |
| 40000-49999 | UDP | TURN relay |
| 6379 | TCP | KV store (localhost only) |
## State and configuration files
```
/etc/livekit/
livekitctl-state.json # Bootstrap state
secrets.json # Generated API keys and secrets
livekit.yaml # LiveKit server config
caddy.json # Caddy config
coturn.conf # TURN server config
/opt/livekit/
bin/
livekit-server # LiveKit binary
```
## DNS setup
Before running bootstrap, create DNS records pointing to your server's public IP:
```
A livekit.example.com → <your-ipv4>
A turn.example.com → <your-ipv4>
```
If your server has IPv6:
```
AAAA livekit.example.com → <your-ipv6>
AAAA turn.example.com → <your-ipv6>
```
The bootstrap command waits for DNS propagation before requesting TLS certificates.
## Global flags
- `--state <path>` - Path to state file (default: /etc/livekit/livekitctl-state.json)

Some files were not shown because too many files have changed in this diff Show More