fix: DM avatar color and reactions in federation + explore/server discovery
- Fix DM welcome header avatar using home identity for correct gradient color - Register DM channel IDs in channelOriginMap so federated DM operations (reactions, messages, typing) route to the correct instance - Pass origin when creating DM channels from friends list and WS events - Add server discovery/explore page with public server listings - Add server visibility and description fields
This commit is contained in:
@@ -68,7 +68,15 @@ export function runMigrations(db: Database.Database): void {
|
||||
name: 'instance_settings',
|
||||
columns: [
|
||||
{ name: 'instance_name', type: "TEXT DEFAULT 'Backspace'" },
|
||||
{ name: 'worker_id', type: 'INTEGER' }
|
||||
{ name: 'worker_id', type: 'INTEGER' },
|
||||
{ name: 'discovery_enabled', type: 'INTEGER NOT NULL DEFAULT 1' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'servers',
|
||||
columns: [
|
||||
{ name: 'visibility', type: "TEXT DEFAULT 'private'" },
|
||||
{ name: 'description', type: 'TEXT' }
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -101,6 +109,20 @@ export function runMigrations(db: Database.Database): void {
|
||||
);
|
||||
`);
|
||||
|
||||
// Ensure join_requests table exists (idempotent)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS join_requests (
|
||||
id TEXT PRIMARY KEY,
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
message TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
decided_by TEXT REFERENCES users(id),
|
||||
created_at INTEGER NOT NULL,
|
||||
decided_at INTEGER
|
||||
);
|
||||
`);
|
||||
|
||||
// ─── RBAC Migration: Ensure @everyone roles exist for all servers ─────────
|
||||
migrateEveryoneRoles(db);
|
||||
|
||||
@@ -110,6 +132,9 @@ export function runMigrations(db: Database.Database): void {
|
||||
// ─── Worker ID: ensure a unique Snowflake worker ID is persisted ───────────
|
||||
migrateWorkerId(db);
|
||||
|
||||
// ─── Namespace replicated users: ensure all federated users use user@domain ─
|
||||
migrateReplicatedUsernames(db);
|
||||
|
||||
// ─── Admin flag: ensure at least one admin exists (first registered user) ──
|
||||
migrateFirstAdmin(db);
|
||||
|
||||
@@ -210,3 +235,23 @@ function migrateEveryoneRoles(db: Database.Database): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename non-namespaced replicated users: e.g. "test" → "test@nova.ddns.net"
|
||||
* Frees plain usernames for native user creation and makes all federated users
|
||||
* visually consistent. Safe because JWTs validate by userId, not username.
|
||||
*/
|
||||
function migrateReplicatedUsernames(db: Database.Database): void {
|
||||
const rows = db.prepare(
|
||||
"SELECT id, username, home_instance FROM users WHERE home_instance IS NOT NULL AND username NOT LIKE '%@%'"
|
||||
).all() as { id: string; username: string; home_instance: string }[];
|
||||
|
||||
if (rows.length === 0) return;
|
||||
|
||||
const update = db.prepare('UPDATE users SET username = ? WHERE id = ?');
|
||||
for (const row of rows) {
|
||||
const newUsername = `${row.username}@${row.home_instance}`;
|
||||
update.run(newUsername, row.id);
|
||||
console.log(`Migrating: Renamed replicated user "${row.username}" → "${newUsername}"`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ export const servers = sqliteTable('servers', {
|
||||
icon: text('icon'),
|
||||
ownerId: text('owner_id').notNull().references(() => users.id),
|
||||
inviteCode: text('invite_code').unique(),
|
||||
visibility: text('visibility').default('private'),
|
||||
description: text('description'),
|
||||
createdAt: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
@@ -182,6 +184,7 @@ export const instanceSettings = sqliteTable('instance_settings', {
|
||||
id: integer('id').primaryKey().default(1),
|
||||
instanceName: text('instance_name').default('Backspace'),
|
||||
workerId: integer('worker_id'),
|
||||
discoveryEnabled: integer('discovery_enabled').notNull().default(1),
|
||||
maxBitrateKbps: integer('max_bitrate_kbps').notNull().default(20000),
|
||||
minBitrateKbps: integer('min_bitrate_kbps').notNull().default(500),
|
||||
bitrateStepKbps: integer('bitrate_step_kbps').notNull().default(500),
|
||||
@@ -191,3 +194,14 @@ export const instanceSettings = sqliteTable('instance_settings', {
|
||||
maxFramerate: integer('max_framerate').notNull().default(60),
|
||||
updatedAt: integer('updated_at').notNull(),
|
||||
});
|
||||
|
||||
export const joinRequests = sqliteTable('join_requests', {
|
||||
id: text('id').primaryKey(),
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
message: text('message'),
|
||||
status: text('status').notNull().default('pending'),
|
||||
decidedBy: text('decided_by').references(() => users.id),
|
||||
createdAt: integer('created_at').notNull(),
|
||||
decidedAt: integer('decided_at'),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user