Skip to content

Commit 2df6552

Browse files
authored
v0.3.55: landing page / OTP improvements, DB package separation, Webhooks fixes, Sharepoint Improvement
2 parents 2149f5e + d0b6945 commit 2df6552

File tree

428 files changed

+2589
-1388
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

428 files changed

+2589
-1388
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ jobs:
3232
env:
3333
NODE_OPTIONS: '--no-warnings'
3434
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
35+
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
3536
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
3637
run: bun run test
3738

3839
- name: Build application
3940
env:
4041
NODE_OPTIONS: '--no-warnings'
4142
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
43+
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
4244
STRIPE_SECRET_KEY: 'dummy_key_for_ci_only'
4345
STRIPE_WEBHOOK_SECRET: 'dummy_secret_for_ci_only'
4446
RESEND_API_KEY: 'dummy_key_for_ci_only'
@@ -71,7 +73,7 @@ jobs:
7173
run: bun install
7274

7375
- name: Apply migrations
74-
working-directory: ./apps/sim
76+
working-directory: ./packages/db
7577
env:
7678
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || secrets.STAGING_DATABASE_URL }}
77-
run: bunx drizzle-kit migrate
79+
run: bunx drizzle-kit migrate --config=./drizzle.config.ts

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,11 @@ Update your `.env` file with the database URL:
125125
DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
126126
```
127127

128-
4. Set up the database:
128+
4. Set up the database (from packages/db):
129129

130130
```bash
131-
bunx drizzle-kit migrate
131+
cd packages/db
132+
bunx drizzle-kit migrate --config=./drizzle.config.ts
132133
```
133134

134135
5. Start the development servers:

apps/docs/content/docs/en/tools/sharepoint.mdx

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Sharepoint
3-
description: Read and create pages
3+
description: Work with pages and lists
44
---
55

66
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -61,7 +61,7 @@ In Sim, the SharePoint integration empowers your agents to create and access Sha
6161

6262
## Usage Instructions
6363

64-
Integrate Sharepoint into the workflow. Can read and create pages, and list sites. Requires OAuth.
64+
Integrate SharePoint into the workflow. Read/create pages, list sites, and work with lists (read, create, update items). Requires OAuth.
6565

6666

6767

@@ -124,6 +124,65 @@ List details of all SharePoint sites
124124
| --------- | ---- | ----------- |
125125
| `site` | object | Information about the current SharePoint site |
126126

127+
### `sharepoint_create_list`
128+
129+
Create a new list in a SharePoint site
130+
131+
#### Input
132+
133+
| Parameter | Type | Required | Description |
134+
| --------- | ---- | -------- | ----------- |
135+
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
136+
| `siteSelector` | string | No | Select the SharePoint site |
137+
| `listDisplayName` | string | Yes | Display name of the list to create |
138+
| `listDescription` | string | No | Description of the list |
139+
| `listTemplate` | string | No | List template name \(e.g., 'genericList'\) |
140+
| `pageContent` | string | No | Optional JSON of columns. Either a top-level array of column definitions or an object with \{ columns: \[...\] \}. |
141+
142+
#### Output
143+
144+
| Parameter | Type | Description |
145+
| --------- | ---- | ----------- |
146+
| `list` | object | Created SharePoint list information |
147+
148+
### `sharepoint_get_list`
149+
150+
Get metadata (and optionally columns/items) for a SharePoint list
151+
152+
#### Input
153+
154+
| Parameter | Type | Required | Description |
155+
| --------- | ---- | -------- | ----------- |
156+
| `siteSelector` | string | No | Select the SharePoint site |
157+
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
158+
| `listId` | string | No | The ID of the list to retrieve |
159+
160+
#### Output
161+
162+
| Parameter | Type | Description |
163+
| --------- | ---- | ----------- |
164+
| `list` | object | Information about the SharePoint list |
165+
166+
### `sharepoint_update_list`
167+
168+
Update the properties (fields) on a SharePoint list item
169+
170+
#### Input
171+
172+
| Parameter | Type | Required | Description |
173+
| --------- | ---- | -------- | ----------- |
174+
| `siteSelector` | string | No | Select the SharePoint site |
175+
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
176+
| `listId` | string | No | The ID of the list containing the item |
177+
| `itemId` | string | Yes | The ID of the list item to update |
178+
| `listItemFields` | object | Yes | Field values to update on the list item |
179+
180+
#### Output
181+
182+
| Parameter | Type | Description |
183+
| --------- | ---- | ----------- |
184+
| `item` | object | Updated SharePoint list item |
185+
127186

128187

129188
## Notes

apps/sim/app/(auth)/components/oauth-provider-checker.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,9 @@ import { env } from '@/lib/env'
44
import { isProd } from '@/lib/environment'
55

66
export async function getOAuthProviderStatus() {
7-
const githubAvailable = !!(
8-
env.GITHUB_CLIENT_ID &&
9-
env.GITHUB_CLIENT_SECRET &&
10-
env.GITHUB_CLIENT_ID !== 'placeholder' &&
11-
env.GITHUB_CLIENT_SECRET !== 'placeholder'
12-
)
7+
const githubAvailable = !!(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
138

14-
const googleAvailable = !!(
15-
env.GOOGLE_CLIENT_ID &&
16-
env.GOOGLE_CLIENT_SECRET &&
17-
env.GOOGLE_CLIENT_ID !== 'placeholder' &&
18-
env.GOOGLE_CLIENT_SECRET !== 'placeholder'
19-
)
9+
const googleAvailable = !!(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET)
2010

2111
return { githubAvailable, googleAvailable, isProduction: isProd }
2212
}

apps/sim/app/(auth)/components/social-login-buttons.tsx

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,6 @@ export function SocialLoginButtons({
3737
setIsGithubLoading(true)
3838
try {
3939
await client.signIn.social({ provider: 'github', callbackURL })
40-
41-
// Mark that the user has previously logged in
42-
if (typeof window !== 'undefined') {
43-
localStorage.setItem('has_logged_in_before', 'true')
44-
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax' // 1 year expiry
45-
}
4640
} catch (err: any) {
4741
let errorMessage = 'Failed to sign in with GitHub'
4842

@@ -66,13 +60,6 @@ export function SocialLoginButtons({
6660
setIsGoogleLoading(true)
6761
try {
6862
await client.signIn.social({ provider: 'google', callbackURL })
69-
70-
// Mark that the user has previously logged in
71-
if (typeof window !== 'undefined') {
72-
localStorage.setItem('has_logged_in_before', 'true')
73-
// Also set a cookie to enable middleware to check login status
74-
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax' // 1 year expiry
75-
}
7663
} catch (err: any) {
7764
let errorMessage = 'Failed to sign in with Google'
7865

apps/sim/app/(auth)/login/login-form.tsx

Lines changed: 8 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ const validatePassword = (passwordValue: string): string[] => {
7474

7575
if (!PASSWORD_VALIDATIONS.required.test(passwordValue)) {
7676
errors.push(PASSWORD_VALIDATIONS.required.message)
77-
return errors // Return early for required field
77+
return errors
7878
}
7979

8080
if (!PASSWORD_VALIDATIONS.notEmpty.test(passwordValue)) {
8181
errors.push(PASSWORD_VALIDATIONS.notEmpty.message)
82-
return errors // Return early for empty field
82+
return errors
8383
}
8484

8585
return errors
@@ -104,11 +104,9 @@ export default function LoginPage({
104104
const [showValidationError, setShowValidationError] = useState(false)
105105
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
106106

107-
// Initialize state for URL parameters
108107
const [callbackUrl, setCallbackUrl] = useState('/workspace')
109108
const [isInviteFlow, setIsInviteFlow] = useState(false)
110109

111-
// Forgot password states
112110
const [forgotPasswordOpen, setForgotPasswordOpen] = useState(false)
113111
const [forgotPasswordEmail, setForgotPasswordEmail] = useState('')
114112
const [isSubmittingReset, setIsSubmittingReset] = useState(false)
@@ -117,38 +115,31 @@ export default function LoginPage({
117115
message: string
118116
}>({ type: null, message: '' })
119117

120-
// Email validation state
121118
const [email, setEmail] = useState('')
122119
const [emailErrors, setEmailErrors] = useState<string[]>([])
123120
const [showEmailValidationError, setShowEmailValidationError] = useState(false)
124121

125-
// Extract URL parameters after component mounts to avoid SSR issues
126122
useEffect(() => {
127123
setMounted(true)
128124

129-
// Only access search params on the client side
130125
if (searchParams) {
131126
const callback = searchParams.get('callbackUrl')
132127
if (callback) {
133-
// Validate the callbackUrl before setting it
134128
if (validateCallbackUrl(callback)) {
135129
setCallbackUrl(callback)
136130
} else {
137131
logger.warn('Invalid callback URL detected and blocked:', { url: callback })
138-
// Keep the default safe value ('/workspace')
139132
}
140133
}
141134

142135
const inviteFlow = searchParams.get('invite_flow') === 'true'
143136
setIsInviteFlow(inviteFlow)
144137
}
145138

146-
// Check if CSS variable has been customized
147139
const checkCustomBrand = () => {
148140
const computedStyle = getComputedStyle(document.documentElement)
149141
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
150142

151-
// Check if the CSS variable exists and is different from the default
152143
if (brandAccent && brandAccent !== '#6f3dfa') {
153144
setButtonClass('auth-button-custom')
154145
} else {
@@ -158,7 +149,6 @@ export default function LoginPage({
158149

159150
checkCustomBrand()
160151

161-
// Also check on window resize or theme changes
162152
window.addEventListener('resize', checkCustomBrand)
163153
const observer = new MutationObserver(checkCustomBrand)
164154
observer.observe(document.documentElement, {
@@ -189,7 +179,6 @@ export default function LoginPage({
189179
const newEmail = e.target.value
190180
setEmail(newEmail)
191181

192-
// Silently validate but don't show errors until submit
193182
const errors = validateEmailField(newEmail)
194183
setEmailErrors(errors)
195184
setShowEmailValidationError(false)
@@ -199,7 +188,6 @@ export default function LoginPage({
199188
const newPassword = e.target.value
200189
setPassword(newPassword)
201190

202-
// Silently validate but don't show errors until submit
203191
const errors = validatePassword(newPassword)
204192
setPasswordErrors(errors)
205193
setShowValidationError(false)
@@ -210,26 +198,23 @@ export default function LoginPage({
210198
setIsLoading(true)
211199

212200
const formData = new FormData(e.currentTarget)
213-
const email = formData.get('email') as string
201+
const emailRaw = formData.get('email') as string
202+
const email = emailRaw.trim().toLowerCase()
214203

215-
// Validate email on submit
216204
const emailValidationErrors = validateEmailField(email)
217205
setEmailErrors(emailValidationErrors)
218206
setShowEmailValidationError(emailValidationErrors.length > 0)
219207

220-
// Validate password on submit
221208
const passwordValidationErrors = validatePassword(password)
222209
setPasswordErrors(passwordValidationErrors)
223210
setShowValidationError(passwordValidationErrors.length > 0)
224211

225-
// If there are validation errors, stop submission
226212
if (emailValidationErrors.length > 0 || passwordValidationErrors.length > 0) {
227213
setIsLoading(false)
228214
return
229215
}
230216

231217
try {
232-
// Final validation before submission
233218
const safeCallbackUrl = validateCallbackUrl(callbackUrl) ? callbackUrl : '/workspace'
234219

235220
const result = await client.signIn.email(
@@ -291,33 +276,13 @@ export default function LoginPage({
291276
setIsLoading(false)
292277
return
293278
}
294-
295-
// Mark that the user has previously logged in
296-
if (typeof window !== 'undefined') {
297-
localStorage.setItem('has_logged_in_before', 'true')
298-
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax' // 1 year expiry
299-
}
300279
} catch (err: any) {
301-
// Handle only the special verification case that requires a redirect
302280
if (err.message?.includes('not verified') || err.code?.includes('EMAIL_NOT_VERIFIED')) {
303-
try {
304-
await client.emailOtp.sendVerificationOtp({
305-
email,
306-
type: 'email-verification',
307-
})
308-
309-
if (typeof window !== 'undefined') {
310-
sessionStorage.setItem('verificationEmail', email)
311-
}
312-
313-
router.push('/verify')
314-
return
315-
} catch (_verifyErr) {
316-
setPasswordErrors(['Failed to send verification code. Please try again later.'])
317-
setShowValidationError(true)
318-
setIsLoading(false)
319-
return
281+
if (typeof window !== 'undefined') {
282+
sessionStorage.setItem('verificationEmail', email)
320283
}
284+
router.push('/verify')
285+
return
321286
}
322287

323288
console.error('Uncaught login error:', err)

apps/sim/app/(auth)/reset-password/page.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ function ResetPasswordContent() {
2424
text: '',
2525
})
2626

27-
// Validate token presence
2827
useEffect(() => {
2928
if (!token) {
3029
setStatusMessage({
@@ -60,7 +59,6 @@ function ResetPasswordContent() {
6059
text: 'Password reset successful! Redirecting to login...',
6160
})
6261

63-
// Redirect to login page after 1.5 seconds
6462
setTimeout(() => {
6563
router.push('/login?resetSuccess=true')
6664
}, 1500)

apps/sim/app/(auth)/reset-password/reset-password-form.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,10 @@ export function RequestResetForm({
3030
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
3131

3232
useEffect(() => {
33-
// Check if CSS variable has been customized
3433
const checkCustomBrand = () => {
3534
const computedStyle = getComputedStyle(document.documentElement)
3635
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
3736

38-
// Check if the CSS variable exists and is different from the default
3937
if (brandAccent && brandAccent !== '#6f3dfa') {
4038
setButtonClass('auth-button-custom')
4139
} else {
@@ -45,7 +43,6 @@ export function RequestResetForm({
4543

4644
checkCustomBrand()
4745

48-
// Also check on window resize or theme changes
4946
window.addEventListener('resize', checkCustomBrand)
5047
const observer = new MutationObserver(checkCustomBrand)
5148
observer.observe(document.documentElement, {
@@ -132,12 +129,10 @@ export function SetNewPasswordForm({
132129
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
133130

134131
useEffect(() => {
135-
// Check if CSS variable has been customized
136132
const checkCustomBrand = () => {
137133
const computedStyle = getComputedStyle(document.documentElement)
138134
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
139135

140-
// Check if the CSS variable exists and is different from the default
141136
if (brandAccent && brandAccent !== '#6f3dfa') {
142137
setButtonClass('auth-button-custom')
143138
} else {
@@ -147,7 +142,6 @@ export function SetNewPasswordForm({
147142

148143
checkCustomBrand()
149144

150-
// Also check on window resize or theme changes
151145
window.addEventListener('resize', checkCustomBrand)
152146
const observer = new MutationObserver(checkCustomBrand)
153147
observer.observe(document.documentElement, {
@@ -164,7 +158,6 @@ export function SetNewPasswordForm({
164158
const handleSubmit = async (e: React.FormEvent) => {
165159
e.preventDefault()
166160

167-
// Simple validation
168161
if (password.length < 8) {
169162
setValidationMessage('Password must be at least 8 characters long')
170163
return

0 commit comments

Comments
 (0)