Initial commit
This commit is contained in:
11
.claude-plugin/plugin.json
Normal file
11
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "insforge",
|
||||||
|
"description": "Official InsForge plugin for Claude Code with skills, templates, and commands for building full-stack applications with InsForge BaaS",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "InsForge Team"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# insforge
|
||||||
|
|
||||||
|
Official InsForge plugin for Claude Code with skills, templates, and commands for building full-stack applications with InsForge BaaS
|
||||||
45
plugin.lock.json
Normal file
45
plugin.lock.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:InsForge/InsForge:claude-plugin",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "2eb70f9877a9dca7d64d1740e54989796907705c",
|
||||||
|
"treeHash": "5073368ca0e3490b12b2ca68d07b877f3dde4493009d925011f7be752357cfde",
|
||||||
|
"generatedAt": "2025-11-28T10:11:42.280728Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "insforge",
|
||||||
|
"description": "Official InsForge plugin for Claude Code with skills, templates, and commands for building full-stack applications with InsForge BaaS",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "9c12497d77fcd41300571cb56c6e2288c0a6f779a7a3d64bf6054db2dd8380cb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "4c81253fcb6bba04607bf5d62b6aca6a4a5b5af0c41425d4a572247e685f691d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/insforge-schema-patterns/SKILL.md",
|
||||||
|
"sha256": "fe9e04b63b58aaed7f8db53c1ecafce2ee6b2cc2e19b312a69b128c849975bd0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "5073368ca0e3490b12b2ca68d07b877f3dde4493009d925011f7be752357cfde"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
270
skills/insforge-schema-patterns/SKILL.md
Normal file
270
skills/insforge-schema-patterns/SKILL.md
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
---
|
||||||
|
name: insforge-schema-patterns
|
||||||
|
description: Database schema patterns for InsForge including social graphs, e-commerce, content publishing, and multi-tenancy with RLS policies. Use when designing data models with relationships, foreign keys, or Row Level Security.
|
||||||
|
---
|
||||||
|
|
||||||
|
# InsForge Schema Patterns
|
||||||
|
|
||||||
|
Expert patterns for designing PostgreSQL schemas optimized for InsForge's PostgREST backend.
|
||||||
|
|
||||||
|
## Pattern 1: Social Graph (Follows)
|
||||||
|
|
||||||
|
**Use when:** Building social features like Twitter, Instagram, LinkedIn connections
|
||||||
|
|
||||||
|
**Schema:**
|
||||||
|
```sql
|
||||||
|
CREATE TABLE follows (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
follower_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
following_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
UNIQUE(follower_id, following_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Index for fast lookups
|
||||||
|
CREATE INDEX idx_follows_follower ON follows(follower_id);
|
||||||
|
CREATE INDEX idx_follows_following ON follows(following_id);
|
||||||
|
|
||||||
|
-- RLS: Users can read all follows but only create their own
|
||||||
|
ALTER TABLE follows ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY "Anyone can read follows" ON follows
|
||||||
|
FOR SELECT USING (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can follow others" ON follows
|
||||||
|
FOR INSERT
|
||||||
|
TO authenticated
|
||||||
|
WITH CHECK (uid() = follower_id);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can unfollow" ON follows
|
||||||
|
FOR DELETE
|
||||||
|
TO authenticated
|
||||||
|
USING (uid() = follower_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Query with InsForge SDK:**
|
||||||
|
```javascript
|
||||||
|
// Get users I follow with their profiles
|
||||||
|
const { data: following } = await client.database
|
||||||
|
.from('follows')
|
||||||
|
.select('*, following:following_id(id, nickname, avatar_url, bio)')
|
||||||
|
.eq('follower_id', currentUserId);
|
||||||
|
|
||||||
|
// Get my followers
|
||||||
|
const { data: followers } = await client.database
|
||||||
|
.from('follows')
|
||||||
|
.select('*, follower:follower_id(id, nickname, avatar_url, bio)')
|
||||||
|
.eq('following_id', currentUserId);
|
||||||
|
|
||||||
|
// Check if user1 follows user2
|
||||||
|
const { data: isFollowing } = await client.database
|
||||||
|
.from('follows')
|
||||||
|
.select()
|
||||||
|
.eq('follower_id', user1Id)
|
||||||
|
.eq('following_id', user2Id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
// Follow a user
|
||||||
|
await client.database
|
||||||
|
.from('follows')
|
||||||
|
.insert([{ follower_id: currentUserId, following_id: targetUserId }]);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 2: Likes (Many-to-Many Junction Table)
|
||||||
|
|
||||||
|
**Use when:** Users can like posts, comments, or other content
|
||||||
|
|
||||||
|
**Schema:**
|
||||||
|
```sql
|
||||||
|
CREATE TABLE likes (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
UNIQUE(user_id, post_id) -- Prevent duplicate likes
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_likes_post ON likes(post_id);
|
||||||
|
CREATE INDEX idx_likes_user ON likes(user_id);
|
||||||
|
|
||||||
|
ALTER TABLE likes ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY "Anyone can read likes" ON likes
|
||||||
|
FOR SELECT USING (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can like posts" ON likes
|
||||||
|
FOR INSERT
|
||||||
|
TO authenticated
|
||||||
|
WITH CHECK (uid() = user_id);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can unlike their likes" ON likes
|
||||||
|
FOR DELETE
|
||||||
|
TO authenticated
|
||||||
|
USING (uid() = user_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Query with InsForge SDK:**
|
||||||
|
```javascript
|
||||||
|
// Get post with like count and whether current user liked it
|
||||||
|
const { data: post } = await client.database
|
||||||
|
.from('posts')
|
||||||
|
.select(`
|
||||||
|
*,
|
||||||
|
likes(count),
|
||||||
|
user_like:likes!inner(id, user_id)
|
||||||
|
`)
|
||||||
|
.eq('id', postId)
|
||||||
|
.eq('user_like.user_id', currentUserId)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
// Like a post
|
||||||
|
await client.database
|
||||||
|
.from('likes')
|
||||||
|
.insert([{ user_id: currentUserId, post_id: postId }]);
|
||||||
|
|
||||||
|
// Unlike a post
|
||||||
|
await client.database
|
||||||
|
.from('likes')
|
||||||
|
.delete()
|
||||||
|
.eq('user_id', currentUserId)
|
||||||
|
.eq('post_id', postId);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 3: Nested Comments (Self-Referential)
|
||||||
|
|
||||||
|
**Use when:** Building comment threads, nested replies
|
||||||
|
|
||||||
|
**Schema:**
|
||||||
|
```sql
|
||||||
|
CREATE TABLE comments (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
|
||||||
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
parent_comment_id UUID REFERENCES comments(id) ON DELETE CASCADE,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_comments_post ON comments(post_id);
|
||||||
|
CREATE INDEX idx_comments_parent ON comments(parent_comment_id);
|
||||||
|
|
||||||
|
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY "Anyone can read comments" ON comments
|
||||||
|
FOR SELECT USING (true);
|
||||||
|
|
||||||
|
CREATE POLICY "Authenticated users can comment" ON comments
|
||||||
|
FOR INSERT
|
||||||
|
TO authenticated
|
||||||
|
WITH CHECK (uid() = user_id);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can edit their comments" ON comments
|
||||||
|
FOR UPDATE
|
||||||
|
TO authenticated
|
||||||
|
USING (uid() = user_id)
|
||||||
|
WITH CHECK (uid() = user_id);
|
||||||
|
|
||||||
|
CREATE POLICY "Users can delete their comments" ON comments
|
||||||
|
FOR DELETE
|
||||||
|
TO authenticated
|
||||||
|
USING (uid() = user_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Query with InsForge SDK:**
|
||||||
|
```javascript
|
||||||
|
// Get top-level comments with author info
|
||||||
|
const { data: comments } = await client.database
|
||||||
|
.from('comments')
|
||||||
|
.select('*, author:user_id(nickname, avatar_url)')
|
||||||
|
.eq('post_id', postId)
|
||||||
|
.is('parent_comment_id', null)
|
||||||
|
.order('created_at', { ascending: false });
|
||||||
|
|
||||||
|
// Get replies to a comment
|
||||||
|
const { data: replies } = await client.database
|
||||||
|
.from('comments')
|
||||||
|
.select('*, author:user_id(nickname, avatar_url)')
|
||||||
|
.eq('parent_comment_id', commentId)
|
||||||
|
.order('created_at', { ascending: true });
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 4: Multi-Tenant (Organization Scoped)
|
||||||
|
|
||||||
|
**Use when:** Building SaaS apps where data is scoped to organizations/workspaces
|
||||||
|
|
||||||
|
**Schema:**
|
||||||
|
```sql
|
||||||
|
CREATE TABLE organizations (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE organization_members (
|
||||||
|
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
|
||||||
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
|
||||||
|
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
PRIMARY KEY (organization_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE projects (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- RLS: Users can only see projects in their organizations
|
||||||
|
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY "Users see org projects" ON projects
|
||||||
|
FOR SELECT
|
||||||
|
TO authenticated
|
||||||
|
USING (
|
||||||
|
organization_id IN (
|
||||||
|
SELECT organization_id
|
||||||
|
FROM organization_members
|
||||||
|
WHERE user_id = uid()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE POLICY "Admins can create projects" ON projects
|
||||||
|
FOR INSERT
|
||||||
|
TO authenticated
|
||||||
|
WITH CHECK (
|
||||||
|
organization_id IN (
|
||||||
|
SELECT organization_id
|
||||||
|
FROM organization_members
|
||||||
|
WHERE user_id = uid()
|
||||||
|
AND role IN ('owner', 'admin')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always add indexes** on foreign key columns for performance
|
||||||
|
2. **Use UNIQUE constraints** on junction tables to prevent duplicates
|
||||||
|
3. **Enable RLS** on all user-facing tables
|
||||||
|
4. **Use ON DELETE CASCADE** for automatic cleanup
|
||||||
|
5. **Foreign key expansion** in SDK uses the syntax: `table:column(fields)`
|
||||||
|
6. **Count aggregations** use: `table(count)`
|
||||||
|
7. **Filter nested tables** with: `nested_table!inner()` for inner join behavior
|
||||||
|
|
||||||
|
## Common Mistakes to Avoid
|
||||||
|
|
||||||
|
- ❌ Forgetting indexes on foreign keys → Slow queries
|
||||||
|
- ❌ Not using UNIQUE on junction tables → Duplicate likes/follows
|
||||||
|
- ❌ Missing RLS policies → Data leaks
|
||||||
|
- ❌ Using `.single()` on queries that might return multiple rows → Errors
|
||||||
|
- ❌ Not wrapping INSERT data in arrays → PostgREST error
|
||||||
Reference in New Issue
Block a user