Release/v0.2.31 (#188)

This commit is contained in:
Doh
2026-03-07 09:53:36 -05:00
committed by GitHub
parent b01c5dcd6a
commit 62b5ea41b3
21 changed files with 548 additions and 56 deletions

View File

@@ -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
View 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',
}

View File

@@ -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 {

View File

@@ -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
View 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,
})),
}
}

View File

@@ -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,