mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 16:55:37 +02:00
⭐ feat: Expand zero-sync schema with users, teams, and Steam integration (#275)
## Description <!-- Briefly describe the purpose and scope of your changes --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Expanded data model to include users, Steam accounts, teams, members, and friends lists for richer user and team management. - Introduced detailed relationships and row-level permissions for enhanced access control. - **Chores** - Updated dependency version for improved compatibility. - Adjusted environment variables and configuration for improved performance and reliability. - Updated development scripts for clearer SQL permissions generation and workflow separation. - Enhanced .gitignore to exclude SQL files from version control. - **Refactor** - Restructured schema and permissions logic for greater flexibility and security. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,60 +1,162 @@
|
||||
import { type Limitations } from "@nestri/core/src/steam/steam.sql";
|
||||
import {
|
||||
json,
|
||||
table,
|
||||
string,
|
||||
number,
|
||||
string,
|
||||
createSchema,
|
||||
relationships,
|
||||
definePermissions,
|
||||
type ExpressionBuilder,
|
||||
} from "@rocicorp/zero";
|
||||
|
||||
// TODO: Add Steam, and Machines here
|
||||
|
||||
const timestamps = {
|
||||
time_created: number(),
|
||||
time_deleted: number().optional(),
|
||||
} as const;
|
||||
|
||||
const team = table("team")
|
||||
// Table Definitions
|
||||
const users = table("users")
|
||||
.columns({
|
||||
id: string(),
|
||||
email: string(),
|
||||
avatar_url: string().optional(),
|
||||
last_login: number(),
|
||||
name: string(),
|
||||
polar_customer_id: string().optional(),
|
||||
...timestamps
|
||||
})
|
||||
.primaryKey("id");
|
||||
|
||||
const steam_accounts = table("steam_accounts")
|
||||
.columns({
|
||||
steam_id: string(),
|
||||
user_id: string(),
|
||||
status: string(),
|
||||
last_synced_at: number(),
|
||||
real_name: string().optional(),
|
||||
member_since: number(),
|
||||
name: string(),
|
||||
profile_url: string().optional(),
|
||||
username: string(),
|
||||
avatar_hash: string(),
|
||||
limitations: json<Limitations>(),
|
||||
...timestamps,
|
||||
})
|
||||
.primaryKey("steam_id");
|
||||
|
||||
const teams = table("teams")
|
||||
.columns({
|
||||
id: string(),
|
||||
name: string(),
|
||||
owner_id: string(),
|
||||
invite_code: string(),
|
||||
slug: string(),
|
||||
plan_type: string<"Hosted" | "BYOG">(),
|
||||
max_members: number(),
|
||||
...timestamps,
|
||||
})
|
||||
.primaryKey("id");
|
||||
|
||||
const member = table("member")
|
||||
const members = table("members")
|
||||
.columns({
|
||||
id: string(),
|
||||
email: string(),
|
||||
team_id: string(),
|
||||
time_created: number(),
|
||||
time_seen: number().optional(),
|
||||
time_deleted: number().optional(),
|
||||
user_id: string().optional(),
|
||||
steam_id: string(),
|
||||
role: string(),
|
||||
...timestamps,
|
||||
})
|
||||
.primaryKey("team_id", "id");
|
||||
.primaryKey("team_id", "steam_id");
|
||||
|
||||
export const schema = createSchema(1, {
|
||||
tables: [team, member],
|
||||
const friends_list = table("friends_list")
|
||||
.columns({
|
||||
steam_id: string(),
|
||||
friend_steam_id: string(),
|
||||
...timestamps,
|
||||
})
|
||||
.primaryKey("steam_id", "friend_steam_id");
|
||||
|
||||
// Schema and Relationships
|
||||
export const schema = createSchema({
|
||||
tables: [users, steam_accounts, teams, members, friends_list],
|
||||
relationships: [
|
||||
relationships(member, (r) => ({
|
||||
team: r.one({
|
||||
sourceField: ["team_id"],
|
||||
destSchema: team,
|
||||
relationships(steam_accounts, (r) => ({
|
||||
user: r.one({
|
||||
sourceField: ["user_id"],
|
||||
destSchema: users,
|
||||
destField: ["id"],
|
||||
}),
|
||||
memberEntries: r.many({
|
||||
sourceField: ["steam_id"],
|
||||
destSchema: members,
|
||||
destField: ["steam_id"],
|
||||
}),
|
||||
friends: r.many({
|
||||
sourceField: ["steam_id"],
|
||||
destSchema: friends_list,
|
||||
destField: ["steam_id"],
|
||||
}),
|
||||
friendOf: r.many({
|
||||
sourceField: ["steam_id"],
|
||||
destSchema: friends_list,
|
||||
destField: ["friend_steam_id"],
|
||||
}),
|
||||
})),
|
||||
relationships(users, (r) => ({
|
||||
teams: r.many({
|
||||
sourceField: ["id"],
|
||||
destSchema: teams,
|
||||
destField: ["owner_id"],
|
||||
}),
|
||||
members: r.many({
|
||||
sourceField: ["team_id"],
|
||||
destSchema: member,
|
||||
sourceField: ["id"],
|
||||
destSchema: members,
|
||||
destField: ["user_id"],
|
||||
}),
|
||||
})),
|
||||
relationships(teams, (r) => ({
|
||||
owner: r.one({
|
||||
sourceField: ["owner_id"],
|
||||
destSchema: users,
|
||||
destField: ["id"],
|
||||
}),
|
||||
steamAccount: r.one({
|
||||
sourceField: ["owner_id"],
|
||||
destSchema: steam_accounts,
|
||||
destField: ["user_id"],
|
||||
}),
|
||||
members: r.many({
|
||||
sourceField: ["id"],
|
||||
destSchema: members,
|
||||
destField: ["team_id"],
|
||||
}),
|
||||
})),
|
||||
relationships(team, (r) => ({
|
||||
members: r.many({
|
||||
sourceField: ["id"],
|
||||
destSchema: member,
|
||||
destField: ["team_id"],
|
||||
relationships(members, (r) => ({
|
||||
team: r.one({
|
||||
sourceField: ["team_id"],
|
||||
destSchema: teams,
|
||||
destField: ["id"],
|
||||
}),
|
||||
user: r.one({
|
||||
sourceField: ["user_id"],
|
||||
destSchema: users,
|
||||
destField: ["id"],
|
||||
}),
|
||||
steamAccount: r.one({
|
||||
sourceField: ["steam_id"],
|
||||
destSchema: steam_accounts,
|
||||
destField: ["steam_id"],
|
||||
}),
|
||||
})),
|
||||
relationships(friends_list, (r) => ({
|
||||
steam: r.one({
|
||||
sourceField: ["steam_id"],
|
||||
destSchema: steam_accounts,
|
||||
destField: ["steam_id"],
|
||||
}),
|
||||
friend: r.one({
|
||||
sourceField: ["friend_steam_id"],
|
||||
destSchema: steam_accounts,
|
||||
destField: ["steam_id"],
|
||||
}),
|
||||
})),
|
||||
],
|
||||
@@ -72,18 +174,41 @@ type Auth = {
|
||||
|
||||
export const permissions = definePermissions<Auth, Schema>(schema, () => {
|
||||
return {
|
||||
member: {
|
||||
members: {
|
||||
row: {
|
||||
select: [
|
||||
(auth, q) => q.exists("members", (u) => u.where("email", auth.sub)),
|
||||
],
|
||||
(auth: Auth, q: ExpressionBuilder<Schema, 'members'>) => q.exists("user", (u) => u.where("id", auth.sub)),
|
||||
(auth: Auth, q: ExpressionBuilder<Schema, 'members'>) => q.exists("steamAccount", (u) => u.where("user_id", auth.sub)),
|
||||
]
|
||||
},
|
||||
},
|
||||
team: {
|
||||
teams: {
|
||||
row: {
|
||||
select: [
|
||||
(auth, q) => q.exists("members", (u) => u.where("email", auth.sub)),
|
||||
],
|
||||
(auth: Auth, q: ExpressionBuilder<Schema, 'teams'>) => q.exists("members", (u) => u.where("user_id", auth.sub)),
|
||||
]
|
||||
},
|
||||
},
|
||||
steam_accounts: {
|
||||
row: {
|
||||
select: [
|
||||
(auth: Auth, q: ExpressionBuilder<Schema, 'steam_accounts'>) => q.exists("user", (u) => u.where("id", auth.sub)),
|
||||
]
|
||||
},
|
||||
},
|
||||
users: {
|
||||
row: {
|
||||
select: [
|
||||
(auth: Auth, q: ExpressionBuilder<Schema, 'users'>) => q.cmp("id", "=", auth.sub),
|
||||
]
|
||||
},
|
||||
},
|
||||
friends_list: {
|
||||
row: {
|
||||
select: [
|
||||
(auth: Auth, q: ExpressionBuilder<Schema, 'friends_list'>) => q.exists("steam", (u) => u.where("user_id", auth.sub)),
|
||||
(auth: Auth, q: ExpressionBuilder<Schema, 'friends_list'>) => q.exists("friend", (u) => u.where("user_id", auth.sub)),
|
||||
]
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user