Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
dohsimpson committed Feb 19, 2025
1 parent d684789 commit a43e9fe
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 53 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

### BREAKING CHANGE

* Requires AUTH_SECRET environment variable for user authentication
* Generate a secure secret with: `openssl rand -base64 32`
* PLEASE BACK UP `data/` DIRECTORY BEFORE UPGRADE.
* Requires AUTH_SECRET environment variable for user authentication. Generate a secure secret with: `openssl rand -base64 32`
* Previous coin balance will be hidden. If this is undesirable, consider using manual adjustment to adjust coin balance after upgrade.

## Version 0.1.30

Expand Down
15 changes: 1 addition & 14 deletions app/actions/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,6 @@ export async function loadUsersData(): Promise<UserData> {
}

export async function saveUsersData(data: UserData): Promise<void> {
if (process.env.DEMO === 'true') {
// remove password for all users
data.users.map(user => {
user.password = ''
})
}
return saveData('auth', data)
}

Expand All @@ -366,7 +360,7 @@ export async function getUser(username: string, plainTextPassword?: string): Pro
export async function createUser(formData: FormData): Promise<User> {
const username = formData.get('username') as string;
let password = formData.get('password') as string | undefined;
const avatarFile = formData.get('avatar') as File | null;
const avatarPath = formData.get('avatarPath') as string;
const permissions = formData.get('permissions') ?
JSON.parse(formData.get('permissions') as string) as Permission[] :
undefined;
Expand All @@ -384,13 +378,6 @@ export async function createUser(formData: FormData): Promise<User> {

const hashedPassword = password ? saltAndHashPassword(password) : '';

// Handle avatar upload if present
let avatarPath: string | undefined;
if (avatarFile && avatarFile instanceof File && avatarFile.size > 0) {
const avatarFormData = new FormData();
avatarFormData.append('avatar', avatarFile);
avatarPath = await uploadAvatar(avatarFormData);
}

const newUser: User = {
id: uuid(),
Expand Down
2 changes: 2 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import '@/lib/env.server' // startup env var check

import './globals.css'
import { Inter } from 'next/font/google'
import { DM_Sans } from 'next/font/google'
Expand Down
2 changes: 0 additions & 2 deletions components/PasswordEntryForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { User as UserIcon } from 'lucide-react';
import { Permission, User } from '@/lib/types';
import { toast } from '@/hooks/use-toast';
import { useState } from 'react';
import { DEFAULT_ADMIN_PASS, DEFAULT_ADMIN_PASS_HASH } from '@/lib/constants';
import { Switch } from './ui/switch';

interface PasswordEntryFormProps {
user: User;
Expand Down
54 changes: 26 additions & 28 deletions components/UserForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,23 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
const { currentUser } = useHelpers()
const getDefaultPermissions = (): Permission[] => [{
habit: {
write: false,
write: true,
interact: true
},
wishlist: {
write: false,
write: true,
interact: true
},
coins: {
write: false,
write: true,
interact: true
}
}];

const [avatarPath, setAvatarPath] = useState(user?.avatarPath)
const [username, setUsername] = useState(user?.username || '');
const [password, setPassword] = useState<string | undefined>('');
const [disablePassword, setDisablePassword] = useState(user?.password === '');
const [disablePassword, setDisablePassword] = useState(user?.password === '' || process.env.NEXT_PUBLIC_DEMO === 'true');
const [error, setError] = useState('');
const [avatarFile, setAvatarFile] = useState<File | null>(null);
const [isAdmin, setIsAdmin] = useState(user?.isAdmin || false);
Expand Down Expand Up @@ -118,9 +118,7 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
}
formData.append('permissions', JSON.stringify(isAdmin ? undefined : permissions));
formData.append('isAdmin', JSON.stringify(isAdmin));
if (avatarFile) {
formData.append('avatar', avatarFile);
}
formData.append('avatarPath', avatarPath || '');

const newUser = await createUser(formData);
setUsersData(prev => ({
Expand Down Expand Up @@ -153,27 +151,24 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
return;
}

if (isEditing) {
const formData = new FormData();
formData.append('avatar', file);
const formData = new FormData();
formData.append('avatar', file);

try {
const path = await uploadAvatar(formData);
setAvatarPath(path);
toast({
title: "Avatar uploaded",
description: "Successfully uploaded avatar",
variant: 'default'
});
} catch (err) {
toast({
title: "Error",
description: "Failed to upload avatar",
variant: 'destructive'
});
}
} else {
setAvatarFile(file);
try {
const path = await uploadAvatar(formData);
setAvatarPath(path);
setAvatarFile(null); // Clear the file since we've uploaded it
toast({
title: "Avatar uploaded",
description: "Successfully uploaded avatar",
variant: 'default'
});
} catch (err) {
toast({
title: "Error",
description: "Failed to upload avatar",
variant: 'destructive'
});
}
};

Expand Down Expand Up @@ -245,6 +240,9 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
className={error ? 'border-red-500' : ''}
disabled={disablePassword}
/>
{process.env.NEXT_PUBLIC_DEMO === 'true' && (
<p className="text-sm text-red-500">Password is automatically disabled in demo instance</p>
)}
</div>

<div className="flex items-center space-x-2">
Expand All @@ -262,7 +260,7 @@ export default function UserForm({ userId, onCancel, onSuccess }: UserFormProps)
)}


{currentUser && currentUser.isAdmin && users.users.length > 1 && <PermissionSelector
{currentUser && currentUser.isAdmin && <PermissionSelector
permissions={permissions}
isAdmin={isAdmin}
onPermissionsChange={setPermissions}
Expand Down
5 changes: 1 addition & 4 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,4 @@ export const QUICK_DATES = [
{ label: 'Friday', value: 'this friday' },
{ label: 'Saturday', value: 'this saturday' },
{ label: 'Sunday', value: 'this sunday' },
] as const

export const DEFAULT_ADMIN_PASS = "admin"
export const DEFAULT_ADMIN_PASS_HASH = "4fd03f11af068acd8aa2bf8a38ce6ef7:bcf07a1776ba9fcb927fbcfb0eda933573f87e0852f8620b79c1da9242664856197f53109a94233cdaea7e6b08bf07713642f990739ff71480990f842809bd99" // "admin"
] as const
31 changes: 31 additions & 0 deletions lib/env.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { z } from "zod"

const zodEnv = z.object({
AUTH_SECRET: z.string(),
NEXT_PUBLIC_DEMO: z.string().optional(),
})

declare global {
interface ProcessEnv extends z.TypeOf<typeof zodEnv> {
AUTH_SECRET: string;
NEXT_PUBLIC_DEMO?: string;
}
}

try {
zodEnv.parse(process.env)
} catch (err) {
if (err instanceof z.ZodError) {
const { fieldErrors } = err.flatten()
const errorMessage = Object.entries(fieldErrors)
.map(([field, errors]) =>
errors ? `${field}: ${errors.join(", ")}` : field,
)
.join("\n ")

console.error(
`Missing environment variables:\n ${errorMessage}`,
)
process.exit(1)
}
}
4 changes: 1 addition & 3 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { DEFAULT_ADMIN_PASS_HASH } from "./constants"
import { saltAndHashPassword } from "./server-helpers"
import { uuid } from "./utils"

export type UserId = string
Expand Down Expand Up @@ -100,7 +98,7 @@ export const getDefaultUsersData = (): UserData => ({
{
id: uuid(),
username: 'admin',
password: DEFAULT_ADMIN_PASS_HASH,
password: '',
isAdmin: true,
}
]
Expand Down

0 comments on commit a43e9fe

Please sign in to comment.