Portfolio.Web 1.0.0.0
.NET, Blazor, WPF와 서비스 운영 경험을 프로젝트·이력·기술 스택 단위로 보여주는 개발자 포트폴리오입니다.
로딩중...
검색중...
일치하는것 없음
PortfolioHomeViewModel.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.Identity;
4
6
16{
34 private readonly IProjectStore _projects;
43 private readonly PortfolioOptions _opts;
53
62 public List<PortfolioConfig> Tenants { get; private set; } = [];
71 public Dictionary<string, int> ProjectCounts { get; private set; } = [];
80 public string StatusMessage { get; set; } = "";
89 public bool IsAuthenticated { get; private set; }
98 public string LoginPassword { get; set; } = "";
99
100 // 신규 생성 폼
109 public string NewSlug { get; set; } = "";
118 public string NewOwnerName { get; set; } = "";
127 public string NewPassword { get; set; } = "";
128
170 IPortfolioTenantStore tenants,
171 IProjectStore projects,
172 PortfolioOptions opts,
173 PortfolioUserContext userContext)
174 {
175 _tenants = tenants;
176 _projects = projects;
177 _opts = opts;
178 _userContext = userContext;
179 }
180
197 public async Task LoadAsync()
198 {
199 Tenants = await _tenants.GetAllAsync();
200 var counts = new Dictionary<string, int>();
201 foreach (var t in Tenants)
202 {
203 var list = await _projects.GetAllAsync(t.Slug);
204 counts[t.Slug] = list.Count;
205 }
206 ProjectCounts = counts;
207 }
208
225 public async Task<bool> CreateTenantAsync()
226 {
227 StatusMessage = "";
228 if (string.IsNullOrWhiteSpace(NewSlug)) { StatusMessage = "❌ URL 주소를 입력하세요."; return false; }
229 if (string.IsNullOrWhiteSpace(NewOwnerName)) { StatusMessage = "❌ 이름을 입력하세요."; return false; }
230 if (NewPassword.Length < 8) { StatusMessage = "❌ 비밀번호는 8자 이상이어야 합니다."; return false; }
231
232 var slug = NewSlug.Trim().ToLowerInvariant();
233 var existing = await _tenants.GetAsync(slug);
234 if (existing != null) { StatusMessage = "❌ 이미 사용 중인 주소입니다."; return false; }
235
236 var user = await _userContext.GetCurrentAsync().ConfigureAwait(false);
237 var cfg = new PortfolioConfig
238 {
239 Slug = slug,
240 OwnerName = NewOwnerName.Trim(),
241 Title = "개발자",
242 Bio = "",
243 ThemeName = "dark",
244 PasswordHash = DreaminePasswordHasher.HashPassword(NewPassword),
245 ShowOnHome = true,
246 CreatedAt = DateTime.Now,
247 };
248
249 if (user.IsAuthenticated)
250 {
251 cfg.OwnerUserId = user.Id;
252 cfg.OwnerProvider = user.Provider;
253 cfg.OwnerEmail = user.Email;
254 cfg.OwnerDisplayName = user.DisplayName;
255 cfg.OwnerLinkedAt = DateTime.Now;
256 cfg.AdminUsers.Add(new PortfolioAdminUser
257 {
258 UserId = user.Id,
259 Provider = user.Provider,
260 Email = user.Email,
261 DisplayName = user.DisplayName,
262 Role = "Owner",
263 AddedAt = DateTime.Now
264 });
265 }
266
267 await _tenants.SaveAsync(cfg);
268 StatusMessage = $"✅ '{slug}' 포트폴리오가 생성되었습니다!";
269 return true;
270 }
271
288 public Task<bool> LoginAsync()
289 {
290 if (DreaminePasswordHasher.VerifyPassword(LoginPassword, _opts.SuperAdminPassword))
291 {
292 IsAuthenticated = true;
293 StatusMessage = "";
294 return Task.FromResult(true);
295 }
296 StatusMessage = "❌ 비밀번호가 틀렸습니다.";
297 return Task.FromResult(false);
298 }
299
324 public async Task SaveTenantAsync(PortfolioConfig cfg)
325 {
326 await _tenants.SaveAsync(cfg);
327 StatusMessage = $"✅ '{cfg.Slug}' 저장 완료.";
328 }
329
354 public async Task DeleteTenantAsync(string slug)
355 {
356 await _tenants.DeleteAsync(slug);
357 await LoadAsync();
358 StatusMessage = $"✅ '{slug}' 삭제 완료.";
359 }
360
361}
PortfolioHomeViewModel(IPortfolioTenantStore tenants, IProjectStore projects, PortfolioOptions opts, PortfolioUserContext userContext)