refactor: rename Server → Space across entire codebase
Renames all domain terminology from "Server" to "Space" throughout the application — database schema, API routes, shared types, stores, components, and UI strings. Files renamed: ServerSidebar → SpaceSidebar, CreateServer → CreateSpace, JoinServer → JoinSpace, ServerSettings → SpaceSettings, serverStore → spaceStore, routes/servers → routes/spaces.
This commit is contained in:
@@ -29,7 +29,7 @@ function createTables(db: Database.Database): void {
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS servers (
|
||||
CREATE TABLE IF NOT EXISTS spaces (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
icon TEXT,
|
||||
@@ -38,17 +38,17 @@ function createTables(db: Database.Database): void {
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS server_members (
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
CREATE TABLE IF NOT EXISTS space_members (
|
||||
space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
nickname TEXT,
|
||||
joined_at INTEGER NOT NULL,
|
||||
PRIMARY KEY (server_id, user_id)
|
||||
PRIMARY KEY (space_id, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS channels (
|
||||
id TEXT PRIMARY KEY,
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
topic TEXT,
|
||||
@@ -131,7 +131,7 @@ function createTables(db: Database.Database): void {
|
||||
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id TEXT PRIMARY KEY,
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
color TEXT DEFAULT '#b9bbbe',
|
||||
position INTEGER DEFAULT 0,
|
||||
@@ -140,10 +140,10 @@ function createTables(db: Database.Database): void {
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS member_roles (
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
role_id TEXT NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (server_id, user_id, role_id)
|
||||
PRIMARY KEY (space_id, user_id, role_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS channel_overrides (
|
||||
@@ -163,7 +163,7 @@ function createTables(db: Database.Database): void {
|
||||
PRIMARY KEY (user_id, channel_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS server_folders (
|
||||
CREATE TABLE IF NOT EXISTS space_folders (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
name TEXT,
|
||||
@@ -172,10 +172,10 @@ function createTables(db: Database.Database): void {
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS server_folder_members (
|
||||
folder_id TEXT NOT NULL REFERENCES server_folders(id) ON DELETE CASCADE,
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (folder_id, server_id)
|
||||
CREATE TABLE IF NOT EXISTS space_folder_members (
|
||||
folder_id TEXT NOT NULL REFERENCES space_folders(id) ON DELETE CASCADE,
|
||||
space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (folder_id, space_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS instance_settings (
|
||||
|
||||
@@ -73,7 +73,7 @@ export function runMigrations(db: Database.Database): void {
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'servers',
|
||||
name: 'spaces',
|
||||
columns: [
|
||||
{ name: 'visibility', type: "TEXT DEFAULT 'private'" },
|
||||
{ name: 'description', type: 'TEXT' }
|
||||
@@ -113,7 +113,7 @@ export function runMigrations(db: Database.Database): void {
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS join_requests (
|
||||
id TEXT PRIMARY KEY,
|
||||
server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
|
||||
space_id TEXT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
message TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
@@ -123,7 +123,7 @@ export function runMigrations(db: Database.Database): void {
|
||||
);
|
||||
`);
|
||||
|
||||
// ─── RBAC Migration: Ensure @everyone roles exist for all servers ─────────
|
||||
// ─── RBAC Migration: Ensure @everyone roles exist for all spaces ─────────
|
||||
migrateEveryoneRoles(db);
|
||||
|
||||
// ─── Instance settings: ensure default row exists ──────────────────────────
|
||||
@@ -182,58 +182,58 @@ function migrateWorkerId(db: Database.Database): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** For each server, ensure an @everyone role exists with id === server.id */
|
||||
/** For each space, ensure an @everyone role exists with id === space.id */
|
||||
function migrateEveryoneRoles(db: Database.Database): void {
|
||||
const servers = db.prepare('SELECT id FROM servers').all() as { id: string }[];
|
||||
const spaces = db.prepare('SELECT id FROM spaces').all() as { id: string }[];
|
||||
const now = Date.now();
|
||||
const defaultPerms = permissionsToString(DEFAULT_EVERYONE_PERMISSIONS);
|
||||
const adminPerms = permissionsToString(ALL_PERMISSIONS);
|
||||
|
||||
const insertRole = db.prepare(
|
||||
'INSERT OR IGNORE INTO roles (id, server_id, name, color, position, permissions, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)'
|
||||
'INSERT OR IGNORE INTO roles (id, space_id, name, color, position, permissions, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
|
||||
for (const server of servers) {
|
||||
// Create @everyone role if it doesn't exist (id = server.id)
|
||||
insertRole.run(server.id, server.id, '@everyone', '#b9bbbe', 0, defaultPerms, now);
|
||||
for (const space of spaces) {
|
||||
// Create @everyone role if it doesn't exist (id = space.id)
|
||||
insertRole.run(space.id, space.id, '@everyone', '#b9bbbe', 0, defaultPerms, now);
|
||||
}
|
||||
|
||||
// Migrate existing admin members: ensure an Admin role exists and assign it
|
||||
// Only run if the old `role` column still exists on server_members
|
||||
const smColumns = db.pragma('table_info(server_members)') as { name: string }[];
|
||||
// Only run if the old `role` column still exists on space_members
|
||||
const smColumns = db.pragma('table_info(space_members)') as { name: string }[];
|
||||
const hasRoleColumn = smColumns.some(c => c.name === 'role');
|
||||
const adminMembers = hasRoleColumn
|
||||
? db.prepare("SELECT server_id, user_id FROM server_members WHERE role = 'admin'").all() as { server_id: string; user_id: string }[]
|
||||
? db.prepare("SELECT space_id, user_id FROM space_members WHERE role = 'admin'").all() as { space_id: string; user_id: string }[]
|
||||
: [];
|
||||
|
||||
if (adminMembers.length > 0) {
|
||||
// Group by server
|
||||
const serverAdmins = new Map<string, string[]>();
|
||||
// Group by space
|
||||
const spaceAdmins = new Map<string, string[]>();
|
||||
for (const row of adminMembers) {
|
||||
let arr = serverAdmins.get(row.server_id);
|
||||
if (!arr) { arr = []; serverAdmins.set(row.server_id, arr); }
|
||||
let arr = spaceAdmins.get(row.space_id);
|
||||
if (!arr) { arr = []; spaceAdmins.set(row.space_id, arr); }
|
||||
arr.push(row.user_id);
|
||||
}
|
||||
|
||||
const checkAdminRole = db.prepare(
|
||||
"SELECT id FROM roles WHERE server_id = ? AND name = 'Admin' AND permissions = ?"
|
||||
"SELECT id FROM roles WHERE space_id = ? AND name = 'Admin' AND permissions = ?"
|
||||
);
|
||||
const insertMemberRole = db.prepare(
|
||||
'INSERT OR IGNORE INTO member_roles (server_id, user_id, role_id) VALUES (?, ?, ?)'
|
||||
'INSERT OR IGNORE INTO member_roles (space_id, user_id, role_id) VALUES (?, ?, ?)'
|
||||
);
|
||||
|
||||
for (const [serverId, userIds] of serverAdmins) {
|
||||
// Find or create Admin role for this server
|
||||
let adminRole = checkAdminRole.get(serverId, adminPerms) as { id: string } | undefined;
|
||||
for (const [spaceId, userIds] of spaceAdmins) {
|
||||
// Find or create Admin role for this space
|
||||
let adminRole = checkAdminRole.get(spaceId, adminPerms) as { id: string } | undefined;
|
||||
if (!adminRole) {
|
||||
// Generate a simple unique ID for the admin role
|
||||
const adminRoleId = `${serverId}-admin`;
|
||||
insertRole.run(adminRoleId, serverId, 'Admin', '#e74c3c', 1, adminPerms, now);
|
||||
const adminRoleId = `${spaceId}-admin`;
|
||||
insertRole.run(adminRoleId, spaceId, 'Admin', '#e74c3c', 1, adminPerms, now);
|
||||
adminRole = { id: adminRoleId };
|
||||
}
|
||||
|
||||
for (const userId of userIds) {
|
||||
insertMemberRole.run(serverId, userId, adminRole.id);
|
||||
insertMemberRole.run(spaceId, userId, adminRole.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const users = sqliteTable('users', {
|
||||
createdAt: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
export const servers = sqliteTable('servers', {
|
||||
export const spaces = sqliteTable('spaces', {
|
||||
id: text('id').primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
icon: text('icon'),
|
||||
@@ -26,18 +26,18 @@ export const servers = sqliteTable('servers', {
|
||||
createdAt: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
export const serverMembers = sqliteTable('server_members', {
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
export const spaceMembers = sqliteTable('space_members', {
|
||||
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
|
||||
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
nickname: text('nickname'),
|
||||
joinedAt: integer('joined_at').notNull(),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.serverId, table.userId] }),
|
||||
pk: primaryKey({ columns: [table.spaceId, table.userId] }),
|
||||
}));
|
||||
|
||||
export const channels = sqliteTable('channels', {
|
||||
id: text('id').primaryKey(),
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull(),
|
||||
type: text('type').notNull(),
|
||||
topic: text('topic'),
|
||||
@@ -129,7 +129,7 @@ export const dmReactions = sqliteTable('dm_reactions', {
|
||||
|
||||
export const roles = sqliteTable('roles', {
|
||||
id: text('id').primaryKey(),
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull(),
|
||||
color: text('color').default('#b9bbbe'),
|
||||
position: integer('position').default(0),
|
||||
@@ -138,11 +138,11 @@ export const roles = sqliteTable('roles', {
|
||||
});
|
||||
|
||||
export const memberRoles = sqliteTable('member_roles', {
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
|
||||
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
roleId: text('role_id').notNull().references(() => roles.id, { onDelete: 'cascade' }),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.serverId, table.userId, table.roleId] }),
|
||||
pk: primaryKey({ columns: [table.spaceId, table.userId, table.roleId] }),
|
||||
}));
|
||||
|
||||
export const channelOverrides = sqliteTable('channel_overrides', {
|
||||
@@ -164,7 +164,7 @@ export const readStates = sqliteTable('read_states', {
|
||||
pk: primaryKey({ columns: [table.userId, table.channelId] }),
|
||||
}));
|
||||
|
||||
export const serverFolders = sqliteTable('server_folders', {
|
||||
export const spaceFolders = sqliteTable('space_folders', {
|
||||
id: text('id').primaryKey(),
|
||||
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
name: text('name'),
|
||||
@@ -173,11 +173,11 @@ export const serverFolders = sqliteTable('server_folders', {
|
||||
createdAt: integer('created_at').notNull(),
|
||||
});
|
||||
|
||||
export const serverFolderMembers = sqliteTable('server_folder_members', {
|
||||
folderId: text('folder_id').notNull().references(() => serverFolders.id, { onDelete: 'cascade' }),
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
export const spaceFolderMembers = sqliteTable('space_folder_members', {
|
||||
folderId: text('folder_id').notNull().references(() => spaceFolders.id, { onDelete: 'cascade' }),
|
||||
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
|
||||
}, (table) => ({
|
||||
pk: primaryKey({ columns: [table.folderId, table.serverId] }),
|
||||
pk: primaryKey({ columns: [table.folderId, table.spaceId] }),
|
||||
}));
|
||||
|
||||
export const instanceSettings = sqliteTable('instance_settings', {
|
||||
@@ -197,7 +197,7 @@ export const instanceSettings = sqliteTable('instance_settings', {
|
||||
|
||||
export const joinRequests = sqliteTable('join_requests', {
|
||||
id: text('id').primaryKey(),
|
||||
serverId: text('server_id').notNull().references(() => servers.id, { onDelete: 'cascade' }),
|
||||
spaceId: text('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }),
|
||||
userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
message: text('message'),
|
||||
status: text('status').notNull().default('pending'),
|
||||
|
||||
@@ -7,8 +7,8 @@ import { DEFAULT_EVERYONE_PERMISSIONS, permissionsToString } from '@backspace/sh
|
||||
export async function seedDatabase(): Promise<void> {
|
||||
const db = getDb();
|
||||
|
||||
const existingServers = db.select().from(schema.servers).all();
|
||||
if (existingServers.length > 0) {
|
||||
const existingSpaces = db.select().from(schema.spaces).all();
|
||||
if (existingSpaces.length > 0) {
|
||||
console.log('Database already has data, skipping seed');
|
||||
return;
|
||||
}
|
||||
@@ -28,17 +28,17 @@ export async function seedDatabase(): Promise<void> {
|
||||
createdAt: Date.now(),
|
||||
}).run();
|
||||
|
||||
const serverId = generateSnowflake();
|
||||
db.insert(schema.servers).values({
|
||||
id: serverId,
|
||||
const spaceId = generateSnowflake();
|
||||
db.insert(schema.spaces).values({
|
||||
id: spaceId,
|
||||
name: 'Backspace',
|
||||
ownerId: adminId,
|
||||
inviteCode: 'backspace',
|
||||
createdAt: Date.now(),
|
||||
}).run();
|
||||
|
||||
db.insert(schema.serverMembers).values({
|
||||
serverId: serverId,
|
||||
db.insert(schema.spaceMembers).values({
|
||||
spaceId: spaceId,
|
||||
userId: adminId,
|
||||
joinedAt: Date.now(),
|
||||
}).run();
|
||||
@@ -46,7 +46,7 @@ export async function seedDatabase(): Promise<void> {
|
||||
const generalChannelId = generateSnowflake();
|
||||
db.insert(schema.channels).values({
|
||||
id: generalChannelId,
|
||||
serverId: serverId,
|
||||
spaceId: spaceId,
|
||||
name: 'general',
|
||||
type: 'text',
|
||||
topic: 'General discussion',
|
||||
@@ -57,17 +57,17 @@ export async function seedDatabase(): Promise<void> {
|
||||
const voiceChannelId = generateSnowflake();
|
||||
db.insert(schema.channels).values({
|
||||
id: voiceChannelId,
|
||||
serverId: serverId,
|
||||
spaceId: spaceId,
|
||||
name: 'General Voice',
|
||||
type: 'voice',
|
||||
position: 1,
|
||||
createdAt: Date.now(),
|
||||
}).run();
|
||||
|
||||
// Create @everyone role (id === serverId convention)
|
||||
// Create @everyone role (id === spaceId convention)
|
||||
db.insert(schema.roles).values({
|
||||
id: serverId,
|
||||
serverId: serverId,
|
||||
id: spaceId,
|
||||
spaceId: spaceId,
|
||||
name: '@everyone',
|
||||
color: '#b9bbbe',
|
||||
position: 0,
|
||||
@@ -76,6 +76,6 @@ export async function seedDatabase(): Promise<void> {
|
||||
}).run();
|
||||
|
||||
console.log('Database seeded successfully');
|
||||
console.log(` Default server: Backspace (invite code: backspace)`);
|
||||
console.log(` Default space: Backspace (invite code: backspace)`);
|
||||
console.log(` Admin user: admin / admin123`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user