mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-03-11 04:49:49 +01:00
Release/v0.2.31 (#188)
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
getDefaultWishlistData,
|
||||
Habit,
|
||||
ViewType,
|
||||
getDefaultUsersData,
|
||||
getDefaultPublicUsersData,
|
||||
CompletionCache,
|
||||
getDefaultServerSettings,
|
||||
UserId,
|
||||
@@ -42,7 +42,7 @@ export const browserSettingsAtom = atomWithStorage('browserSettings', {
|
||||
expandedWishlist: false
|
||||
} as BrowserSettings)
|
||||
|
||||
export const usersAtom = atom(getDefaultUsersData())
|
||||
export const usersAtom = atom(getDefaultPublicUsersData())
|
||||
export const settingsAtom = atom(getDefaultSettings());
|
||||
export const habitsAtom = atom(getDefaultHabitsData());
|
||||
export const coinsAtom = atom(getDefaultCoinsData());
|
||||
|
||||
25
lib/avatar.ts
Normal file
25
lib/avatar.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export const ALLOWED_AVATAR_EXTENSIONS = new Set([
|
||||
'.png',
|
||||
'.jpg',
|
||||
'.jpeg',
|
||||
'.gif',
|
||||
'.webp',
|
||||
'.avif',
|
||||
])
|
||||
|
||||
export const ALLOWED_AVATAR_MIME_TYPES = new Set([
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
'image/avif',
|
||||
])
|
||||
|
||||
export const AVATAR_CONTENT_TYPE: Record<string, string> = {
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.webp': 'image/webp',
|
||||
'.avif': 'image/avif',
|
||||
}
|
||||
@@ -1,8 +1,19 @@
|
||||
import { auth } from '@/auth'
|
||||
import 'server-only'
|
||||
import { User, UserId } from './types'
|
||||
import { loadUsersData } from '@/app/actions/data'
|
||||
import { User, UserData, UserId, getDefaultUsersData } from './types'
|
||||
import { randomBytes, scryptSync } from 'crypto'
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
|
||||
async function loadUsersDataFromStore(): Promise<UserData> {
|
||||
try {
|
||||
const filePath = path.join(process.cwd(), 'data', 'auth.json')
|
||||
const data = await fs.readFile(filePath, 'utf8')
|
||||
return JSON.parse(data) as UserData
|
||||
} catch {
|
||||
return getDefaultUsersData()
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCurrentUserId(): Promise<UserId | undefined> {
|
||||
const session = await auth()
|
||||
@@ -15,7 +26,7 @@ export async function getCurrentUser(): Promise<User | undefined> {
|
||||
if (!currentUserId) {
|
||||
return undefined
|
||||
}
|
||||
const usersData = await loadUsersData()
|
||||
const usersData = await loadUsersDataFromStore()
|
||||
return usersData.users.find((u) => u.id === currentUserId)
|
||||
}
|
||||
export function saltAndHashPassword(password: string, salt?: string): string {
|
||||
|
||||
18
lib/types.ts
18
lib/types.ts
@@ -28,6 +28,7 @@ export type SafeUser = SessionUser & {
|
||||
avatarPath?: string
|
||||
permissions?: Permission[]
|
||||
isAdmin?: boolean
|
||||
hasPassword?: boolean
|
||||
}
|
||||
|
||||
export type User = SafeUser & {
|
||||
@@ -35,6 +36,10 @@ export type User = SafeUser & {
|
||||
lastNotificationReadTimestamp?: string // UTC ISO date string
|
||||
}
|
||||
|
||||
export type PublicUser = Omit<User, 'password'> & {
|
||||
hasPassword: boolean
|
||||
}
|
||||
|
||||
export type Habit = {
|
||||
id: string
|
||||
name: string
|
||||
@@ -82,6 +87,10 @@ export interface UserData {
|
||||
users: User[]
|
||||
}
|
||||
|
||||
export interface PublicUserData {
|
||||
users: PublicUser[]
|
||||
}
|
||||
|
||||
export interface HabitsData {
|
||||
habits: Habit[];
|
||||
}
|
||||
@@ -111,6 +120,13 @@ export const getDefaultUsersData = (): UserData => ({
|
||||
]
|
||||
});
|
||||
|
||||
export const getDefaultPublicUsersData = (): PublicUserData => ({
|
||||
users: getDefaultUsersData().users.map(({ password, ...user }) => ({
|
||||
...user,
|
||||
hasPassword: !!password,
|
||||
})),
|
||||
});
|
||||
|
||||
export const getDefaultHabitsData = (): HabitsData => ({
|
||||
habits: []
|
||||
});
|
||||
@@ -192,7 +208,7 @@ export interface JotaiHydrateInitialValues {
|
||||
coins: CoinsData;
|
||||
habits: HabitsData;
|
||||
wishlist: WishlistData;
|
||||
users: UserData;
|
||||
users: PublicUserData;
|
||||
serverSettings: ServerSettings;
|
||||
}
|
||||
|
||||
|
||||
10
lib/user-sanitizer.ts
Normal file
10
lib/user-sanitizer.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { PublicUserData, UserData } from './types'
|
||||
|
||||
export function sanitizeUserData(data: UserData): PublicUserData {
|
||||
return {
|
||||
users: data.users.map(({ password, ...user }) => ({
|
||||
...user,
|
||||
hasPassword: !!password,
|
||||
})),
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { DateTime, DateTimeFormatOptions } from "luxon"
|
||||
import { datetime, RRule } from 'rrule'
|
||||
import { Freq, Habit, CoinTransaction, Permission, ParsedFrequencyResult, ParsedResultType, User, Settings, HabitsData, CoinsData, WishlistData, UserData } from '@/lib/types'
|
||||
import { Freq, Habit, CoinTransaction, Permission, ParsedFrequencyResult, ParsedResultType, User, Settings, HabitsData, CoinsData, WishlistData, UserData, PublicUserData } from '@/lib/types'
|
||||
import { DUE_MAP, INITIAL_DUE, RECURRENCE_RULE_MAP } from "./constants"
|
||||
import * as chrono from 'chrono-node'
|
||||
import _ from "lodash"
|
||||
@@ -497,7 +497,7 @@ export function prepareDataForHashing(
|
||||
habits: HabitsData,
|
||||
coins: CoinsData,
|
||||
wishlist: WishlistData,
|
||||
users: UserData
|
||||
users: UserData | PublicUserData
|
||||
): string {
|
||||
// Combine all data into a single object.
|
||||
// The order of keys in this object itself doesn't matter due to stableStringify,
|
||||
|
||||
Reference in New Issue
Block a user