launchbase 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/launchbase.js +1 -1
- package/package.json +1 -1
- package/template/frontend/src/lib/api.ts +4 -0
- package/template/frontend/src/lib/auth.tsx +9 -2
- package/template/prisma/schema.prisma +6 -1
- package/template/prisma/seed.ts +4 -31
- package/template/src/modules/auth/auth.service.ts +29 -6
- package/template/src/modules/billing/billing.service.ts +1 -1
- package/template/src/modules/deployments/dto/create-deployment.dto.ts +1 -1
- package/template/src/modules/edge-functions/edge-functions.service.ts +6 -5
package/bin/launchbase.js
CHANGED
|
@@ -7,7 +7,7 @@ const crypto = require('crypto');
|
|
|
7
7
|
const fs = require('fs-extra');
|
|
8
8
|
const { execSync, spawn } = require('child_process');
|
|
9
9
|
|
|
10
|
-
const VERSION = '1.2.
|
|
10
|
+
const VERSION = '1.2.3';
|
|
11
11
|
const program = new Command();
|
|
12
12
|
|
|
13
13
|
function findAvailablePort(startPort = 5432, maxAttempts = 100) {
|
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ export const api = axios.create({
|
|
|
14
14
|
api.interceptors.request.use((config) => {
|
|
15
15
|
const token = localStorage.getItem('accessToken')
|
|
16
16
|
const orgId = localStorage.getItem('currentOrgId')
|
|
17
|
+
const projectId = localStorage.getItem('currentProjectId')
|
|
17
18
|
|
|
18
19
|
if (token) {
|
|
19
20
|
config.headers.Authorization = `Bearer ${token}`
|
|
@@ -21,6 +22,9 @@ api.interceptors.request.use((config) => {
|
|
|
21
22
|
if (orgId) {
|
|
22
23
|
config.headers['X-Org-Id'] = orgId
|
|
23
24
|
}
|
|
25
|
+
if (projectId) {
|
|
26
|
+
config.headers['X-Project-Id'] = projectId
|
|
27
|
+
}
|
|
24
28
|
|
|
25
29
|
return config
|
|
26
30
|
})
|
|
@@ -35,23 +35,29 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|
|
35
35
|
|
|
36
36
|
const login = async (email: string, password: string) => {
|
|
37
37
|
const res = await api.post('/api/auth/login', { email, password })
|
|
38
|
-
const { accessToken, refreshToken, user, org } = res.data
|
|
38
|
+
const { accessToken, refreshToken, user, org, project } = res.data
|
|
39
39
|
localStorage.setItem('accessToken', accessToken)
|
|
40
40
|
localStorage.setItem('refreshToken', refreshToken)
|
|
41
41
|
if (org) {
|
|
42
42
|
localStorage.setItem('currentOrgId', org.id)
|
|
43
43
|
}
|
|
44
|
+
if (project) {
|
|
45
|
+
localStorage.setItem('currentProjectId', project.id)
|
|
46
|
+
}
|
|
44
47
|
setUser(user)
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
const signup = async (email: string, password: string, orgName: string) => {
|
|
48
51
|
const res = await api.post('/api/auth/signup', { email, password, orgName })
|
|
49
|
-
const { accessToken, refreshToken, user, org } = res.data
|
|
52
|
+
const { accessToken, refreshToken, user, org, project } = res.data
|
|
50
53
|
localStorage.setItem('accessToken', accessToken)
|
|
51
54
|
localStorage.setItem('refreshToken', refreshToken)
|
|
52
55
|
if (org) {
|
|
53
56
|
localStorage.setItem('currentOrgId', org.id)
|
|
54
57
|
}
|
|
58
|
+
if (project) {
|
|
59
|
+
localStorage.setItem('currentProjectId', project.id)
|
|
60
|
+
}
|
|
55
61
|
setUser(user)
|
|
56
62
|
}
|
|
57
63
|
|
|
@@ -59,6 +65,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|
|
59
65
|
localStorage.removeItem('accessToken')
|
|
60
66
|
localStorage.removeItem('refreshToken')
|
|
61
67
|
localStorage.removeItem('currentOrgId')
|
|
68
|
+
localStorage.removeItem('currentProjectId')
|
|
62
69
|
setUser(null)
|
|
63
70
|
}
|
|
64
71
|
|
|
@@ -111,6 +111,7 @@ model EdgeFunction {
|
|
|
111
111
|
projectId String
|
|
112
112
|
name String
|
|
113
113
|
slug String
|
|
114
|
+
description String?
|
|
114
115
|
runtime String @default("nodejs18")
|
|
115
116
|
sourceCode String
|
|
116
117
|
environment Json?
|
|
@@ -118,6 +119,7 @@ model EdgeFunction {
|
|
|
118
119
|
status String @default("draft")
|
|
119
120
|
deployedAt DateTime?
|
|
120
121
|
deploymentUrl String?
|
|
122
|
+
deploymentId String?
|
|
121
123
|
createdAt DateTime @default(now())
|
|
122
124
|
updatedAt DateTime @updatedAt
|
|
123
125
|
logs EdgeFunctionLog[]
|
|
@@ -152,6 +154,7 @@ model VectorCollection {
|
|
|
152
154
|
id String @id @default(cuid())
|
|
153
155
|
projectId String
|
|
154
156
|
name String
|
|
157
|
+
description String?
|
|
155
158
|
provider String @default("local")
|
|
156
159
|
dimension Int @default(1536)
|
|
157
160
|
embeddingModel String @default("text-embedding-3-small")
|
|
@@ -169,7 +172,9 @@ model Deployment {
|
|
|
169
172
|
id String @id @default(cuid())
|
|
170
173
|
projectId String
|
|
171
174
|
status String @default("pending")
|
|
172
|
-
version String
|
|
175
|
+
version String
|
|
176
|
+
description String?
|
|
177
|
+
error String?
|
|
173
178
|
deployedAt DateTime?
|
|
174
179
|
platform String?
|
|
175
180
|
url String?
|
package/template/prisma/seed.ts
CHANGED
|
@@ -1,38 +1,11 @@
|
|
|
1
|
-
import { PrismaClient
|
|
2
|
-
import * as bcrypt from 'bcrypt';
|
|
1
|
+
import { PrismaClient } from '@prisma/client';
|
|
3
2
|
|
|
4
3
|
const prisma = new PrismaClient();
|
|
5
4
|
|
|
6
5
|
async function main() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const existing = await prisma.user.findUnique({ where: { email } });
|
|
11
|
-
if (existing) return;
|
|
12
|
-
|
|
13
|
-
const saltRounds = Number(process.env.BCRYPT_SALT_ROUNDS ?? '10');
|
|
14
|
-
const passwordHash = await bcrypt.hash(password, saltRounds);
|
|
15
|
-
|
|
16
|
-
const user = await prisma.user.create({
|
|
17
|
-
data: { email, passwordHash }
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const org = await prisma.organization.create({
|
|
21
|
-
data: { name: 'My Organization', slug: 'my-org' }
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
await prisma.organizationMember.create({
|
|
25
|
-
data: { userId: user.id, organizationId: org.id, role: Role.OWNER }
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
await prisma.project.create({
|
|
29
|
-
data: {
|
|
30
|
-
organizationId: org.id,
|
|
31
|
-
name: 'Default Project',
|
|
32
|
-
slug: 'default-project',
|
|
33
|
-
description: 'Default project created during setup'
|
|
34
|
-
}
|
|
35
|
-
});
|
|
6
|
+
// Production-ready seed - no demo data
|
|
7
|
+
// Users, organizations, and projects are created through the API
|
|
8
|
+
console.log('Database seeded (production mode - no demo data)');
|
|
36
9
|
}
|
|
37
10
|
|
|
38
11
|
main()
|
|
@@ -66,7 +66,7 @@ export class AuthService {
|
|
|
66
66
|
const passwordHash = await this.hashPassword(dto.password);
|
|
67
67
|
|
|
68
68
|
// Generate slug from org name
|
|
69
|
-
const
|
|
69
|
+
const orgSlug = dto.orgName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
70
70
|
|
|
71
71
|
const user = await this.prisma.user.create({
|
|
72
72
|
data: {
|
|
@@ -79,7 +79,7 @@ export class AuthService {
|
|
|
79
79
|
const org = await this.prisma.organization.create({
|
|
80
80
|
data: {
|
|
81
81
|
name: dto.orgName,
|
|
82
|
-
slug,
|
|
82
|
+
slug: orgSlug,
|
|
83
83
|
ownerId: user.id
|
|
84
84
|
}
|
|
85
85
|
});
|
|
@@ -92,11 +92,23 @@ export class AuthService {
|
|
|
92
92
|
}
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
+
// Create default project for the organization
|
|
96
|
+
const projectSlug = 'default-project';
|
|
97
|
+
const project = await this.prisma.project.create({
|
|
98
|
+
data: {
|
|
99
|
+
organizationId: org.id,
|
|
100
|
+
name: 'Default Project',
|
|
101
|
+
slug: projectSlug,
|
|
102
|
+
description: 'Default project created during signup'
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
95
106
|
const tokens = await this.issueTokens(user);
|
|
96
107
|
|
|
97
108
|
return {
|
|
98
109
|
user: { id: user.id, email: user.email },
|
|
99
110
|
org: { id: org.id, name: org.name, slug: org.slug },
|
|
111
|
+
project: { id: project.id, name: project.name, slug: project.slug },
|
|
100
112
|
...tokens
|
|
101
113
|
};
|
|
102
114
|
}
|
|
@@ -104,7 +116,15 @@ export class AuthService {
|
|
|
104
116
|
async login(dto: LoginDto) {
|
|
105
117
|
const user = await this.prisma.user.findUnique({
|
|
106
118
|
where: { email: dto.email },
|
|
107
|
-
include: {
|
|
119
|
+
include: {
|
|
120
|
+
memberships: {
|
|
121
|
+
include: {
|
|
122
|
+
organization: {
|
|
123
|
+
include: { projects: { take: 1 } }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
108
128
|
});
|
|
109
129
|
if (!user || !user.passwordHash) throw new UnauthorizedException('Invalid credentials');
|
|
110
130
|
|
|
@@ -113,12 +133,15 @@ export class AuthService {
|
|
|
113
133
|
|
|
114
134
|
const tokens = await this.issueTokens({ id: user.id, email: user.email });
|
|
115
135
|
|
|
116
|
-
// Get first org for the user
|
|
117
|
-
const firstMembership = user.
|
|
136
|
+
// Get first org and project for the user
|
|
137
|
+
const firstMembership = user.memberships[0];
|
|
138
|
+
const firstOrg = firstMembership?.organization;
|
|
139
|
+
const firstProject = firstOrg?.projects[0];
|
|
118
140
|
|
|
119
141
|
return {
|
|
120
142
|
user: { id: user.id, email: user.email },
|
|
121
|
-
org:
|
|
143
|
+
org: firstOrg ? { id: firstOrg.id, name: firstOrg.name, slug: firstOrg.slug } : null,
|
|
144
|
+
project: firstProject ? { id: firstProject.id, name: firstProject.name, slug: firstProject.slug } : null,
|
|
122
145
|
...tokens
|
|
123
146
|
};
|
|
124
147
|
}
|
|
@@ -191,7 +191,7 @@ export class BillingService {
|
|
|
191
191
|
if (type === 'members' && limits.members === -1) return true;
|
|
192
192
|
|
|
193
193
|
if (type === 'projects') {
|
|
194
|
-
const count = await this.prisma.project.count({ where: { orgId } });
|
|
194
|
+
const count = await this.prisma.project.count({ where: { organizationId: orgId } });
|
|
195
195
|
return count < limits.projects;
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -12,18 +12,19 @@ export class EdgeFunctionsService {
|
|
|
12
12
|
// Validate the function code
|
|
13
13
|
this.validateCode(dto.code);
|
|
14
14
|
|
|
15
|
+
// Generate slug from name
|
|
16
|
+
const slug = dto.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
17
|
+
|
|
15
18
|
const func = await this.prisma.edgeFunction.create({
|
|
16
19
|
data: {
|
|
17
20
|
name: dto.name,
|
|
21
|
+
slug,
|
|
18
22
|
description: dto.description,
|
|
19
|
-
|
|
23
|
+
sourceCode: dto.code,
|
|
20
24
|
runtime: dto.runtime || 'nodejs18',
|
|
21
|
-
timeout: dto.timeout || 30000,
|
|
22
|
-
memory: dto.memory || 128,
|
|
23
25
|
environment: dto.environment || {},
|
|
24
26
|
triggers: dto.triggers || [],
|
|
25
27
|
projectId,
|
|
26
|
-
createdBy: userId,
|
|
27
28
|
},
|
|
28
29
|
});
|
|
29
30
|
|
|
@@ -90,7 +91,7 @@ export class EdgeFunctionsService {
|
|
|
90
91
|
|
|
91
92
|
try {
|
|
92
93
|
// Execute in sandboxed VM
|
|
93
|
-
result = await this.runInSandbox(func.
|
|
94
|
+
result = await this.runInSandbox(func.sourceCode, {
|
|
94
95
|
...dto.payload,
|
|
95
96
|
user: { id: user.id, email: user.email },
|
|
96
97
|
projectId,
|