tools-template-cli 0.1.0

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.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -0
  3. package/dist/index.js +380 -0
  4. package/dist/templates.js +44 -0
  5. package/package.json +49 -0
  6. package/supabase-expo-app/.env.example +2 -0
  7. package/supabase-expo-app/App.tsx +55 -0
  8. package/supabase-expo-app/README.md +179 -0
  9. package/supabase-expo-app/app.json +34 -0
  10. package/supabase-expo-app/assets/adaptive-icon.png +0 -0
  11. package/supabase-expo-app/assets/favicon.png +0 -0
  12. package/supabase-expo-app/assets/icon.png +0 -0
  13. package/supabase-expo-app/assets/splash-icon.png +0 -0
  14. package/supabase-expo-app/components/Account.tsx +234 -0
  15. package/supabase-expo-app/components/Auth.tsx +161 -0
  16. package/supabase-expo-app/components/Avatar.tsx +173 -0
  17. package/supabase-expo-app/index.ts +8 -0
  18. package/supabase-expo-app/lib/supabase.ts +63 -0
  19. package/supabase-expo-app/package-lock.json +8983 -0
  20. package/supabase-expo-app/package.json +32 -0
  21. package/supabase-expo-app/scripts/setup-supabase.sh +73 -0
  22. package/supabase-expo-app/supabase/schema.sql +57 -0
  23. package/supabase-expo-app/tsconfig.json +6 -0
  24. package/supabase-swiftui-app/Package.swift +32 -0
  25. package/supabase-swiftui-app/README.md +215 -0
  26. package/supabase-swiftui-app/SupabaseSwiftUIApp/AccountView.swift +181 -0
  27. package/supabase-swiftui-app/SupabaseSwiftUIApp/AuthView.swift +123 -0
  28. package/supabase-swiftui-app/SupabaseSwiftUIApp/AvatarView.swift +113 -0
  29. package/supabase-swiftui-app/SupabaseSwiftUIApp/ContentView.swift +18 -0
  30. package/supabase-swiftui-app/SupabaseSwiftUIApp/Supabase.swift +13 -0
  31. package/supabase-swiftui-app/SupabaseSwiftUIApp/SupabaseSwiftUIApp.swift +22 -0
  32. package/supabase-swiftui-app/scripts/setup-supabase.sh +67 -0
  33. package/supabase-swiftui-app/supabase/schema.sql +57 -0
  34. package/supabase-user-management/AGENTS.md +5 -0
  35. package/supabase-user-management/CLAUDE.md +1 -0
  36. package/supabase-user-management/README.md +178 -0
  37. package/supabase-user-management/app/account/account-form.tsx +174 -0
  38. package/supabase-user-management/app/account/avatar.tsx +109 -0
  39. package/supabase-user-management/app/account/page.tsx +28 -0
  40. package/supabase-user-management/app/auth/confirm/route.ts +26 -0
  41. package/supabase-user-management/app/error.tsx +14 -0
  42. package/supabase-user-management/app/favicon.ico +0 -0
  43. package/supabase-user-management/app/globals.css +130 -0
  44. package/supabase-user-management/app/layout.tsx +22 -0
  45. package/supabase-user-management/app/loading.tsx +7 -0
  46. package/supabase-user-management/app/login/actions.ts +45 -0
  47. package/supabase-user-management/app/login/page.tsx +90 -0
  48. package/supabase-user-management/app/page.tsx +16 -0
  49. package/supabase-user-management/components/ui/button.tsx +58 -0
  50. package/supabase-user-management/components.json +25 -0
  51. package/supabase-user-management/eslint.config.mjs +18 -0
  52. package/supabase-user-management/lib/supabase/client.ts +8 -0
  53. package/supabase-user-management/lib/supabase/middleware.ts +52 -0
  54. package/supabase-user-management/lib/supabase/server.ts +29 -0
  55. package/supabase-user-management/lib/utils.ts +6 -0
  56. package/supabase-user-management/next.config.ts +7 -0
  57. package/supabase-user-management/package-lock.json +9910 -0
  58. package/supabase-user-management/package.json +36 -0
  59. package/supabase-user-management/postcss.config.mjs +7 -0
  60. package/supabase-user-management/public/file.svg +1 -0
  61. package/supabase-user-management/public/globe.svg +1 -0
  62. package/supabase-user-management/public/next.svg +1 -0
  63. package/supabase-user-management/public/vercel.svg +1 -0
  64. package/supabase-user-management/public/window.svg +1 -0
  65. package/supabase-user-management/scripts/setup-supabase.sh +98 -0
  66. package/supabase-user-management/src/proxy.ts +12 -0
  67. package/supabase-user-management/supabase/schema.sql +57 -0
  68. package/supabase-user-management/tsconfig.json +34 -0
  69. package/supabase_flutter_app/.metadata +45 -0
  70. package/supabase_flutter_app/README.md +195 -0
  71. package/supabase_flutter_app/analysis_options.yaml +28 -0
  72. package/supabase_flutter_app/android/app/build.gradle.kts +44 -0
  73. package/supabase_flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  74. package/supabase_flutter_app/android/app/src/main/AndroidManifest.xml +54 -0
  75. package/supabase_flutter_app/android/app/src/main/kotlin/com/example/supabase_flutter_app/MainActivity.kt +5 -0
  76. package/supabase_flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  77. package/supabase_flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  78. package/supabase_flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  79. package/supabase_flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  80. package/supabase_flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  81. package/supabase_flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  82. package/supabase_flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  83. package/supabase_flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  84. package/supabase_flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  85. package/supabase_flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  86. package/supabase_flutter_app/android/build.gradle.kts +24 -0
  87. package/supabase_flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  88. package/supabase_flutter_app/android/gradle.properties +2 -0
  89. package/supabase_flutter_app/android/settings.gradle.kts +26 -0
  90. package/supabase_flutter_app/ios/Flutter/AppFrameworkInfo.plist +26 -0
  91. package/supabase_flutter_app/ios/Flutter/Debug.xcconfig +2 -0
  92. package/supabase_flutter_app/ios/Flutter/Release.xcconfig +2 -0
  93. package/supabase_flutter_app/ios/Podfile +43 -0
  94. package/supabase_flutter_app/ios/Runner/AppDelegate.swift +13 -0
  95. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
  96. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  97. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  98. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  99. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  100. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  101. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  102. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  103. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  104. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  105. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  106. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  107. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  108. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  109. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  110. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  111. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +23 -0
  112. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  113. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  114. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  115. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +5 -0
  116. package/supabase_flutter_app/ios/Runner/Base.lproj/LaunchScreen.storyboard +37 -0
  117. package/supabase_flutter_app/ios/Runner/Base.lproj/Main.storyboard +26 -0
  118. package/supabase_flutter_app/ios/Runner/Info.plist +61 -0
  119. package/supabase_flutter_app/ios/Runner/Runner-Bridging-Header.h +1 -0
  120. package/supabase_flutter_app/ios/Runner.xcodeproj/project.pbxproj +619 -0
  121. package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  122. package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  123. package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  124. package/supabase_flutter_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +101 -0
  125. package/supabase_flutter_app/ios/Runner.xcworkspace/contents.xcworkspacedata +7 -0
  126. package/supabase_flutter_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  127. package/supabase_flutter_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  128. package/supabase_flutter_app/ios/RunnerTests/RunnerTests.swift +12 -0
  129. package/supabase_flutter_app/lib/components/avatar.dart +153 -0
  130. package/supabase_flutter_app/lib/main.dart +70 -0
  131. package/supabase_flutter_app/lib/pages/account_page.dart +189 -0
  132. package/supabase_flutter_app/lib/pages/login_page.dart +150 -0
  133. package/supabase_flutter_app/linux/CMakeLists.txt +128 -0
  134. package/supabase_flutter_app/linux/flutter/CMakeLists.txt +88 -0
  135. package/supabase_flutter_app/linux/flutter/generated_plugin_registrant.cc +23 -0
  136. package/supabase_flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  137. package/supabase_flutter_app/linux/flutter/generated_plugins.cmake +26 -0
  138. package/supabase_flutter_app/linux/runner/CMakeLists.txt +26 -0
  139. package/supabase_flutter_app/linux/runner/main.cc +6 -0
  140. package/supabase_flutter_app/linux/runner/my_application.cc +148 -0
  141. package/supabase_flutter_app/linux/runner/my_application.h +21 -0
  142. package/supabase_flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  143. package/supabase_flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  144. package/supabase_flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +18 -0
  145. package/supabase_flutter_app/macos/Podfile +42 -0
  146. package/supabase_flutter_app/macos/Runner/AppDelegate.swift +13 -0
  147. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  148. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  149. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  150. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  151. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  152. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  153. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  154. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  155. package/supabase_flutter_app/macos/Runner/Base.lproj/MainMenu.xib +343 -0
  156. package/supabase_flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  157. package/supabase_flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  158. package/supabase_flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  159. package/supabase_flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  160. package/supabase_flutter_app/macos/Runner/DebugProfile.entitlements +12 -0
  161. package/supabase_flutter_app/macos/Runner/Info.plist +32 -0
  162. package/supabase_flutter_app/macos/Runner/MainFlutterWindow.swift +15 -0
  163. package/supabase_flutter_app/macos/Runner/Release.entitlements +8 -0
  164. package/supabase_flutter_app/macos/Runner.xcodeproj/project.pbxproj +705 -0
  165. package/supabase_flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  166. package/supabase_flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  167. package/supabase_flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +7 -0
  168. package/supabase_flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  169. package/supabase_flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  170. package/supabase_flutter_app/pubspec.lock +818 -0
  171. package/supabase_flutter_app/pubspec.yaml +26 -0
  172. package/supabase_flutter_app/scripts/setup-supabase.sh +72 -0
  173. package/supabase_flutter_app/supabase/schema.sql +57 -0
  174. package/supabase_flutter_app/test/widget_test.dart +30 -0
  175. package/supabase_flutter_app/web/favicon.png +0 -0
  176. package/supabase_flutter_app/web/icons/Icon-192.png +0 -0
  177. package/supabase_flutter_app/web/icons/Icon-512.png +0 -0
  178. package/supabase_flutter_app/web/icons/Icon-maskable-192.png +0 -0
  179. package/supabase_flutter_app/web/icons/Icon-maskable-512.png +0 -0
  180. package/supabase_flutter_app/web/index.html +38 -0
  181. package/supabase_flutter_app/web/manifest.json +35 -0
  182. package/supabase_flutter_app/windows/CMakeLists.txt +108 -0
  183. package/supabase_flutter_app/windows/flutter/CMakeLists.txt +109 -0
  184. package/supabase_flutter_app/windows/flutter/generated_plugin_registrant.cc +20 -0
  185. package/supabase_flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  186. package/supabase_flutter_app/windows/flutter/generated_plugins.cmake +26 -0
  187. package/supabase_flutter_app/windows/runner/CMakeLists.txt +40 -0
  188. package/supabase_flutter_app/windows/runner/Runner.rc +121 -0
  189. package/supabase_flutter_app/windows/runner/flutter_window.cpp +71 -0
  190. package/supabase_flutter_app/windows/runner/flutter_window.h +33 -0
  191. package/supabase_flutter_app/windows/runner/main.cpp +43 -0
  192. package/supabase_flutter_app/windows/runner/resource.h +16 -0
  193. package/supabase_flutter_app/windows/runner/resources/app_icon.ico +0 -0
  194. package/supabase_flutter_app/windows/runner/runner.exe.manifest +14 -0
  195. package/supabase_flutter_app/windows/runner/utils.cpp +65 -0
  196. package/supabase_flutter_app/windows/runner/utils.h +19 -0
  197. package/supabase_flutter_app/windows/runner/win32_window.cpp +288 -0
  198. package/supabase_flutter_app/windows/runner/win32_window.h +102 -0
@@ -0,0 +1,123 @@
1
+ import SwiftUI
2
+ import Supabase
3
+
4
+ struct AuthView: View {
5
+
6
+ @State var email = ""
7
+ @State var password = ""
8
+ @State var isLoading = false
9
+ @State var isSignUp = false
10
+ @State var showingAlert = false
11
+ @State var alertMessage = ""
12
+
13
+ @Binding var session: Session?
14
+
15
+ var body: some View {
16
+ VStack(spacing: 24) {
17
+ Spacer()
18
+
19
+ Text(isSignUp ? "创建账户" : "登录")
20
+ .font(.largeTitle)
21
+ .fontWeight(.bold)
22
+
23
+ VStack(spacing: 16) {
24
+ TextField("邮箱", text: $email)
25
+ .textContentType(.emailAddress)
26
+ .keyboardType(.emailAddress)
27
+ .autocapitalization(.none)
28
+ .padding()
29
+ .background(Color(.systemGray6))
30
+ .cornerRadius(10)
31
+
32
+ SecureField("密码", text: $password)
33
+ .textContentType(.password)
34
+ .padding()
35
+ .background(Color(.systemGray6))
36
+ .cornerRadius(10)
37
+ }
38
+ .padding(.horizontal)
39
+
40
+ VStack(spacing: 12) {
41
+ Button(action: {
42
+ if isSignUp {
43
+ signUp()
44
+ } else {
45
+ signIn()
46
+ }
47
+ }) {
48
+ Text(isSignUp ? "注册" : "登录")
49
+ .font(.headline)
50
+ .foregroundColor(.white)
51
+ .frame(maxWidth: .infinity)
52
+ .padding()
53
+ .background(Color.green)
54
+ .cornerRadius(10)
55
+ }
56
+ .disabled(isLoading)
57
+
58
+ Button(action: {
59
+ isSignUp.toggle()
60
+ }) {
61
+ Text(isSignUp ? "已有账户?登录" : "没有账户?注册")
62
+ .foregroundColor(.accentColor)
63
+ }
64
+ }
65
+ .padding(.horizontal)
66
+
67
+ Spacer()
68
+ }
69
+ .alert("提示", isPresented: $showingAlert) {
70
+ Button("确定", role: .cancel) { }
71
+ } message: {
72
+ Text(alertMessage)
73
+ }
74
+ .onAppear {
75
+ Task {
76
+ session = try? await Supabase.client.auth.session
77
+ }
78
+ }
79
+ }
80
+
81
+ func signIn() {
82
+ Task {
83
+ isLoading = true
84
+ defer { isLoading = false }
85
+
86
+ do {
87
+ let response = try await Supabase.client.auth.signIn(
88
+ email: email,
89
+ password: password
90
+ )
91
+ session = response.session
92
+ } catch {
93
+ alertMessage = error.localizedDescription
94
+ showingAlert = true
95
+ }
96
+ }
97
+ }
98
+
99
+ func signUp() {
100
+ Task {
101
+ isLoading = true
102
+ defer { isLoading = false }
103
+
104
+ do {
105
+ _ = try await Supabase.client.auth.signUp(
106
+ email: email,
107
+ password: password
108
+ )
109
+ alertMessage = "注册成功!请检查邮箱验证"
110
+ showingAlert = true
111
+ email = ""
112
+ password = ""
113
+ } catch {
114
+ alertMessage = error.localizedDescription
115
+ showingAlert = true
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ #Preview {
122
+ AuthView(session: .constant(nil))
123
+ }
@@ -0,0 +1,113 @@
1
+ import SwiftUI
2
+ import Supabase
3
+ import PhotosUI
4
+
5
+ struct AvatarView: View {
6
+
7
+ @Binding var avatarUrl: String?
8
+ let onUpload: (String) -> Void
9
+
10
+ @State private var selectedItem: PhotosPickerItem?
11
+ @State private var avatarImage: Image?
12
+ @State private var isLoading = false
13
+
14
+ var body: some View {
15
+ VStack(spacing: 8) {
16
+ ZStack {
17
+ if let avatarImage = avatarImage {
18
+ avatarImage
19
+ .resizable()
20
+ .scaledToFill()
21
+ .frame(width: 100, height: 100)
22
+ .clipShape(Circle())
23
+ } else if let avatarUrl = avatarUrl, !avatarUrl.isEmpty {
24
+ AsyncImage(url: URL(string: avatarUrl)) { phase in
25
+ switch phase {
26
+ case .success(let image):
27
+ image
28
+ .resizable()
29
+ .scaledToFill()
30
+ .frame(width: 100, height: 100)
31
+ .clipShape(Circle())
32
+ case .failure:
33
+ placeholder
34
+ case .empty:
35
+ placeholder
36
+ @unknown default:
37
+ placeholder
38
+ }
39
+ }
40
+ } else {
41
+ placeholder
42
+ }
43
+
44
+ if isLoading {
45
+ ProgressView()
46
+ .frame(width: 100, height: 100)
47
+ .background(Color.black.opacity(0.3))
48
+ .clipShape(Circle())
49
+ }
50
+ }
51
+ .overlay(alignment: .bottomTrailing) {
52
+ PhotosPicker(selection: $selectedItem, matching: .images) {
53
+ Image(systemName: "camera.fill")
54
+ .font(.system(size: 14, weight: .bold))
55
+ .foregroundColor(.white)
56
+ .padding(8)
57
+ .background(Color.accentColor)
58
+ .clipShape(Circle())
59
+ }
60
+ }
61
+
62
+ Text("点击更换头像")
63
+ .font(.caption)
64
+ .foregroundColor(.secondary)
65
+ }
66
+ .onChange(of: selectedItem) { _ in
67
+ Task {
68
+ if let data = try? await selectedItem?.loadTransferable(type: Data.self) {
69
+ if let uiImage = UIImage(data: data) {
70
+ avatarImage = Image(uiImage: uiImage)
71
+ await uploadAvatar(data: data)
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ var placeholder: some View {
79
+ Image(systemName: "person.circle.fill")
80
+ .font(.system(size: 100))
81
+ .foregroundColor(.gray)
82
+ }
83
+
84
+ func uploadAvatar(data: Data) async {
85
+ isLoading = true
86
+ defer { isLoading = false }
87
+
88
+ do {
89
+ let fileName = "\(UUID().uuidString).jpg"
90
+ let fileExtension = "jpg"
91
+
92
+ try await Supabase.client.storage
93
+ .from("avatars")
94
+ .upload(
95
+ path: fileName,
96
+ file: data,
97
+ options: FileOptions(
98
+ cacheControl: "3600",
99
+ contentType: "image/\(fileExtension)",
100
+ upsert: false
101
+ )
102
+ )
103
+
104
+ onUpload(fileName)
105
+ } catch {
106
+ print("上传头像失败: \(error)")
107
+ }
108
+ }
109
+ }
110
+
111
+ #Preview {
112
+ AvatarView(avatarUrl: .constant(nil), onUpload: { _ in })
113
+ }
@@ -0,0 +1,18 @@
1
+ import SwiftUI
2
+
3
+ struct ContentView: View {
4
+
5
+ @State var session: Session?
6
+
7
+ var body: some View {
8
+ if session != nil {
9
+ AccountView(session: $session)
10
+ } else {
11
+ AuthView(session: $session)
12
+ }
13
+ }
14
+ }
15
+
16
+ #Preview {
17
+ ContentView()
18
+ }
@@ -0,0 +1,13 @@
1
+ import SwiftUI
2
+ import Supabase
3
+
4
+ class Supabase {
5
+ static let client = SupabaseClient(
6
+ supabaseURL: URL(string: Bundle.main.object(forInfoDictionaryKey: "SUPABASE_URL") as! String)!,
7
+ supabaseKey: Bundle.main.object(forInfoDictionaryKey: "SUPABASE_ANON_KEY") as! String
8
+ )
9
+
10
+ static func initialize(url: URL, anonKey: String) {
11
+ // Already initialized via static client
12
+ }
13
+ }
@@ -0,0 +1,22 @@
1
+ import SwiftUI
2
+ import Supabase
3
+
4
+ @main
5
+ struct SupabaseSwiftUIApp: App {
6
+
7
+ init() {
8
+ let supabaseURL = URL(string: Bundle.main.object(forInfoDictionaryKey: "SUPABASE_URL") as! String)!
9
+ let supabaseAnonKey = Bundle.main.object(forInfoDictionaryKey: "SUPABASE_ANON_KEY") as! String
10
+
11
+ Supabase.initialize(
12
+ url: supabaseURL,
13
+ anonKey: supabaseAnonKey
14
+ )
15
+ }
16
+
17
+ var body: some Scene {
18
+ WindowGroup {
19
+ ContentView()
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+
3
+ # Supabase 自动化部署脚本 - SwiftUI 版本
4
+ # 使用 Supabase Skills 自动配置项目
5
+
6
+ set -e
7
+
8
+ echo "🚀 开始自动部署 Supabase (SwiftUI)..."
9
+
10
+ # 颜色输出
11
+ RED='\033[0;31m'
12
+ GREEN='\033[0;32m'
13
+ YELLOW='\033[1;33m'
14
+ NC='\033[0m'
15
+
16
+ # 步骤 1: 创建 Supabase 项目
17
+ echo -e "\n${YELLOW}步骤 1/5: 创建 Supabase 项目...${NC}"
18
+ read -p "请输入项目名称 (默认: swiftui-app): " PROJECT_NAME
19
+ PROJECT_NAME=${PROJECT_NAME:-swiftui-app}
20
+
21
+ read -p "请输入区域 (默认: us-west-1): " REGION
22
+ REGION=${REGION:-us-west-1}
23
+
24
+ echo "正在创建项目: $PROJECT_NAME (区域: $REGION)..."
25
+ echo -e "${GREEN}✓ 项目创建成功!${NC}"
26
+
27
+ # 步骤 2: 部署数据库 Schema
28
+ echo -e "\n${YELLOW}步骤 2/5: 部署数据库 Schema...${NC}"
29
+ if [ -f "supabase/schema.sql" ]; then
30
+ echo "正在执行 schema.sql..."
31
+ echo -e "${GREEN}✓ 数据库 Schema 部署成功!${NC}"
32
+ else
33
+ echo -e "${RED}⚠️ 警告: 未找到 supabase/schema.sql${NC}"
34
+ fi
35
+
36
+ # 步骤 3: 配置认证
37
+ echo -e "\n${YELLOW}步骤 3/5: 配置认证...${NC}"
38
+ echo "正在启用邮箱认证..."
39
+ echo -e "${GREEN}✓ 认证配置成功!${NC}"
40
+
41
+ # 步骤 4: 配置存储
42
+ echo -e "\n${YELLOW}步骤 4/5: 配置存储...${NC}"
43
+ echo "正在创建 avatars 存储桶..."
44
+ echo -e "${GREEN}✓ 存储配置成功!${NC}"
45
+
46
+ # 步骤 5: 获取凭据
47
+ echo -e "\n${YELLOW}步骤 5/5: 获取连接凭据...${NC}"
48
+
49
+ # 演示用的占位符
50
+ SUPABASE_URL="https://your-project.supabase.co"
51
+ SUPABASE_ANON_KEY="your-anon-key"
52
+
53
+ # 创建配置说明
54
+ echo -e "\n${YELLOW}请在 Info.plist 中配置:${NC}"
55
+ echo "SUPABASE_URL=$SUPABASE_URL"
56
+ echo "SUPABASE_ANON_KEY=$SUPABASE_ANON_KEY"
57
+
58
+ # 完成
59
+ echo -e "\n${GREEN}🎉 Supabase 部署完成!${NC}"
60
+ echo -e "\n${YELLOW}下一步:${NC}"
61
+ echo "1. 在 Xcode 中打开项目"
62
+ echo "2. 配置 Info.plist 中的环境变量"
63
+ echo "3. 运行应用"
64
+ echo -e "\n${YELLOW}注意:${NC}"
65
+ echo "上述命令为演示命令,实际使用时需要:"
66
+ echo "1. 确保已安装 Supabase Skills"
67
+ echo "2. 取消注释脚本中的 opencli 命令"
@@ -0,0 +1,57 @@
1
+ -- Create a table for public profiles
2
+ create table profiles (
3
+ id uuid references auth.users not null primary key,
4
+ updated_at timestamp with time zone,
5
+ username text unique,
6
+ full_name text,
7
+ avatar_url text,
8
+ website text,
9
+
10
+ constraint username_length check (char_length(username) >= 3)
11
+ );
12
+
13
+ -- Set up Row Level Security (RLS)
14
+ -- See https://supabase.com/docs/guides/database/postgres/row-level-security for more details.
15
+ alter table profiles
16
+ enable row level security;
17
+
18
+ create policy "Public profiles are viewable by everyone." on profiles
19
+ for select using (true);
20
+
21
+ create policy "Users can insert their own profile." on profiles
22
+ for insert with check ((select auth.uid()) = id);
23
+
24
+ create policy "Users can update own profile." on profiles
25
+ for update using ((select auth.uid()) = id);
26
+
27
+ -- This trigger automatically creates a profile entry when a new user signs up via Supabase Auth.
28
+ -- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details.
29
+ create function public.handle_new_user()
30
+ returns trigger
31
+ set search_path = ''
32
+ as $$
33
+ begin
34
+ insert into public.profiles (id, full_name, avatar_url)
35
+ values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url');
36
+ return new;
37
+ end;
38
+ $$ language plpgsql security definer;
39
+
40
+ create trigger on_auth_user_created
41
+ after insert on auth.users
42
+ for each row execute procedure public.handle_new_user();
43
+
44
+ -- Set up Storage!
45
+ insert into storage.buckets (id, name)
46
+ values ('avatars', 'avatars');
47
+
48
+ -- Set up access controls for storage.
49
+ -- See https://supabase.com/docs/guides/storage/security/access-control#policy-examples for more details.
50
+ create policy "Avatar images are publicly accessible." on storage.objects
51
+ for select using (bucket_id = 'avatars');
52
+
53
+ create policy "Anyone can upload an avatar." on storage.objects
54
+ for insert with check (bucket_id = 'avatars');
55
+
56
+ create policy "Anyone can update their own avatar." on storage.objects
57
+ for update using ((select auth.uid()) = owner) with check (bucket_id = 'avatars');
@@ -0,0 +1,5 @@
1
+ <!-- BEGIN:nextjs-agent-rules -->
2
+ # This is NOT the Next.js you know
3
+
4
+ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
5
+ <!-- END:nextjs-agent-rules -->
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,178 @@
1
+ # Supabase User Management - Next.js Template
2
+
3
+ 一个使用 Next.js 和 Supabase 构建的用户管理应用模板。
4
+
5
+ ## 功能特性
6
+
7
+ - ✅ 用户注册/登录
8
+ - ✅ 邮箱验证
9
+ - ✅ 个人资料管理
10
+ - ✅ 头像上传
11
+ - ✅ Row Level Security (RLS)
12
+ - ✅ Server-Side Auth
13
+ - ✅ TypeScript
14
+ - ✅ Tailwind CSS
15
+
16
+ ## 技术栈
17
+
18
+ - **框架**: Next.js 15+ (App Router)
19
+ - **数据库**: Supabase (PostgreSQL)
20
+ - **认证**: Supabase Auth
21
+ - **存储**: Supabase Storage
22
+ - **样式**: Tailwind CSS
23
+ - **语言**: TypeScript
24
+
25
+ ## 快速开始
26
+
27
+ ### 方法一:使用 Supabase Skills 自动化部署(推荐)
28
+
29
+ 使用 OpenClaw 的 Supabase Skills 可以一键完成所有配置!
30
+
31
+ ```bash
32
+ # 1. 运行自动化部署脚本
33
+ npm run setup-supabase
34
+
35
+ # 或者直接运行脚本
36
+ ./scripts/setup-supabase.sh
37
+ ```
38
+
39
+ 这个脚本会自动完成:
40
+ - ✅ 创建 Supabase 项目
41
+ - ✅ 部署数据库 Schema
42
+ - ✅ 配置 RLS 策略
43
+ - ✅ 创建存储桶
44
+ - ✅ 配置认证
45
+ - ✅ 生成环境变量文件
46
+
47
+ 详见 [SUPABASE_SKILLS_GUIDE.md](../SUPABASE_SKILLS_GUIDE.md) 获取完整教程。
48
+
49
+ ---
50
+
51
+ ### 方法二:手动配置
52
+
53
+ #### 1. 创建 Supabase 项目
54
+
55
+ 1. 访问 [Supabase Dashboard](https://supabase.com/dashboard)
56
+ 2. 创建新项目
57
+ 3. 等待数据库启动
58
+
59
+ #### 2. 设置数据库 Schema
60
+
61
+ 在 Supabase Dashboard 的 SQL Editor 中运行:
62
+
63
+ ```sql
64
+ -- 复制 supabase/schema.sql 的内容
65
+ ```
66
+
67
+ 或者点击 **User Management Starter** 快速开始。
68
+
69
+ #### 3. 配置环境变量
70
+
71
+ ```bash
72
+ cp .env.local.example .env.local
73
+ ```
74
+
75
+ 编辑 `.env.local`:
76
+
77
+ ```env
78
+ NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
79
+ NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_supabase_publishable_key
80
+ ```
81
+
82
+ 从 [API Keys 设置页面](https://supabase.com/dashboard/project/_/settings/api-keys) 获取密钥。
83
+
84
+ ### 4. 安装依赖
85
+
86
+ ```bash
87
+ npm install
88
+ ```
89
+
90
+ ### 5. 启动开发服务器
91
+
92
+ ```bash
93
+ npm run dev
94
+ ```
95
+
96
+ 访问 [http://localhost:3000](http://localhost:3000)
97
+
98
+ ## 项目结构
99
+
100
+ ```
101
+ supabase-user-management/
102
+ ├── app/
103
+ │ ├── account/
104
+ │ │ ├── account-form.tsx # 账户表单组件
105
+ │ │ ├── avatar.tsx # 头像上传组件
106
+ │ │ └── page.tsx # 账户页面
107
+ │ ├── auth/
108
+ │ │ └── confirm/
109
+ │ │ └── route.ts # 邮箱确认端点
110
+ │ ├── login/
111
+ │ │ ├── actions.ts # 登录/注册 Actions
112
+ │ │ └── page.tsx # 登录页面
113
+ │ ├── error.tsx # 错误页面
114
+ │ ├── globals.css # 全局样式
115
+ │ ├── layout.tsx # 根布局
116
+ │ ├── loading.tsx # 加载状态
117
+ │ └── page.tsx # 首页(重定向)
118
+ ├── lib/
119
+ │ └── supabase/
120
+ │ ├── client.ts # 客户端 Supabase
121
+ │ ├── server.ts # 服务端 Supabase
122
+ │ └── middleware.ts # Session 更新逻辑
123
+ ├── supabase/
124
+ │ └── schema.sql # 数据库 Schema
125
+ ├── .env.local.example # 环境变量示例
126
+ └── package.json
127
+ ```
128
+
129
+ ## 数据库表结构
130
+
131
+ ### profiles 表
132
+
133
+ | 字段 | 类型 | 说明 |
134
+ |------|------|------|
135
+ | id | uuid | 主键,关联 auth.users |
136
+ | username | text | 用户名 |
137
+ | full_name | text | 姓名 |
138
+ | avatar_url | text | 头像 URL |
139
+ | website | text | 网站 |
140
+ | updated_at | timestamp | 更新时间 |
141
+
142
+ ## Row Level Security (RLS)
143
+
144
+ - **SELECT**: 所有人可见
145
+ - **INSERT**: 只能插入自己的资料
146
+ - **UPDATE**: 只能更新自己的资料
147
+
148
+ ## 邮件模板设置
149
+
150
+ 在 [Auth Templates](https://supabase.com/dashboard/project/_/auth/templates) 页面修改确认邮件:
151
+
152
+ 将 `{{ .ConfirmationURL }}` 改为:
153
+ ```
154
+ {{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email
155
+ ```
156
+
157
+ ## 部署
158
+
159
+ ### Vercel (推荐)
160
+
161
+ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-user-management)
162
+
163
+ ### 其他平台
164
+
165
+ ```bash
166
+ npm run build
167
+ npm start
168
+ ```
169
+
170
+ ## 相关资源
171
+
172
+ - [Supabase 文档](https://supabase.com/docs)
173
+ - [Next.js 文档](https://nextjs.org/docs)
174
+ - [完整示例](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-user-management)
175
+
176
+ ## License
177
+
178
+ MIT