Refactor settings and task management, update API, and enhance type handling

- Replaced `username` and `password` fields with `accessKey` in `useSettings` for improved security.
- Simplified task management in `useTasks`, adding `updateTasks` for bulk operations and refining API endpoint usage.
- Updated `useApi` to dynamically use `VITE_API_URL` and switched to `Bearer` authentication.
- Improved `Task` type definitions with renamed fields, nullable properties, and string-based timestamps.
- Replaced `TaskStatus` enum values with string-based representations for better readability.
This commit is contained in:
2026-03-07 23:14:54 +01:00
parent 353bbea093
commit 25fd10a325
4 changed files with 55 additions and 65 deletions

View File

@@ -1,12 +1,8 @@
import type { Settings } from './useSettings.ts'
import { useCrypto } from './useCrypto.ts' import { useCrypto } from './useCrypto.ts'
import { useStore } from './useStore.ts' import { useStore } from './useStore.ts'
const BASE_URL = 'https://automation.deep-node.de/webhook' const BASE_URL = import.meta.env.VITE_API_URL
interface Settings {
username: string
password: string
}
function isTauri() { function isTauri() {
return typeof window !== 'undefined' && Boolean((window as typeof window & { __TAURI__?: unknown }).__TAURI__) return typeof window !== 'undefined' && Boolean((window as typeof window & { __TAURI__?: unknown }).__TAURI__)
@@ -18,19 +14,18 @@ async function buildAuthHeader(): Promise<string | undefined> {
const settings = await getValue<Settings>('settings') const settings = await getValue<Settings>('settings')
if (!settings) if (!settings)
return undefined return undefined
let { username, password } = settings let { accessKey } = settings
password = decrypt(password) as string accessKey = decrypt(accessKey) as string
if (username && password) { if (accessKey) {
const token = btoa(`${username}:${password}`) return `Bearer ${accessKey}`
return `Basic ${token}`
} }
return undefined return undefined
} }
export function useApi() { export function useApi() {
const apiFetch = async (endpoint: string, options: RequestInit = {}) => { const apiFetch = async (endpoint: string, options: RequestInit = {}) => {
const url = endpoint.startsWith('http') ? endpoint : `${BASE_URL}/${endpoint}` const url = endpoint.startsWith('http') ? endpoint : `${BASE_URL}${endpoint}`
const authHeader = await buildAuthHeader() const authHeader = await buildAuthHeader()
const headers = { const headers = {
@@ -62,13 +57,13 @@ export function useApi() {
const post = (endpoint: string, body: unknown, options: RequestInit = {}) => const post = (endpoint: string, body: unknown, options: RequestInit = {}) =>
apiFetch(endpoint, { ...options, method: 'POST', body: JSON.stringify(body) }) apiFetch(endpoint, { ...options, method: 'POST', body: JSON.stringify(body) })
const put = (endpoint: string, body: unknown, options: RequestInit = {}) => const patch = (endpoint: string, body: unknown, options: RequestInit = {}) =>
apiFetch(endpoint, { ...options, method: 'PUT', body: JSON.stringify(body) }) apiFetch(endpoint, { ...options, method: 'PATCH', body: JSON.stringify(body) })
return { return {
get, get,
post, post,
put, patch,
fetch: apiFetch, fetch: apiFetch,
} }
} }

View File

@@ -3,14 +3,12 @@ import { useCrypto } from './useCrypto.ts'
import { useStore } from './useStore.ts' import { useStore } from './useStore.ts'
export interface Settings { export interface Settings {
username: string accessKey: string
password: string
todayShown: boolean todayShown: boolean
} }
const settingsDefault: Settings = { const settingsDefault: Settings = {
username: '', accessKey: '',
password: '',
todayShown: false, todayShown: false,
} }
const settings = ref<Settings>({ ...settingsDefault }) const settings = ref<Settings>({ ...settingsDefault })
@@ -26,31 +24,31 @@ export function useSettings() {
return return
} }
let password = readSettings.password ?? '' let accessKey = readSettings.accessKey ?? ''
if (password) { if (accessKey) {
try { try {
password = decrypt(password) as string accessKey = decrypt(accessKey) as string
} }
catch (error) { catch (error) {
console.warn('Failed to decrypt stored password:', error) console.warn('Failed to decrypt stored accessKey:', error)
} }
} }
settings.value = { settings.value = {
...settingsDefault, ...settingsDefault,
...readSettings, ...readSettings,
password, accessKey,
} }
} }
const saveSettings = async () => { const saveSettings = async () => {
const encryptedPassword = settings.value.password const encryptedPassword = settings.value.accessKey
? encrypt(settings.value.password) as string ? encrypt(settings.value.accessKey) as string
: '' : ''
await setValue<Settings>('settings', { await setValue<Settings>('settings', {
...settings.value, ...settings.value,
password: encryptedPassword, accessKey: encryptedPassword,
}) })
} }

View File

@@ -1,12 +1,12 @@
import type { Task } from '../types.ts' import type { Task } from '../types.ts'
import { useArrayUnique } from '@vueuse/core' import { useArrayUnique } from '@vueuse/core'
import { ref } from 'vue' import { ref } from 'vue'
import { TaskStatus } from '../types.ts'
import { useApi } from './useApi.ts' import { useApi } from './useApi.ts'
const tasks = ref<Task[]>([]) const tasks = ref<Task[]>([])
const isLoading = ref(false) const isLoading = ref(false)
const error = ref<string | null>(null) const error = ref<string | null>(null)
const endpoint = '/items/pomodays'
export function useTasks() { export function useTasks() {
const api = useApi() const api = useApi()
@@ -17,7 +17,7 @@ export function useTasks() {
isLoading.value = true isLoading.value = true
error.value = null error.value = null
try { try {
const data = await api.get('e5880167-9322-4d7b-8a38-e06bae8a7734/list').then(res => res.json()) const data = await api.get(`${endpoint}?limit=-1`).then(res => res.json())
tasks.value = data.tasks ?? [] tasks.value = data.tasks ?? []
} }
catch (e: any) { catch (e: any) {
@@ -29,29 +29,12 @@ export function useTasks() {
} }
} }
const createTask = async (taskData: Pick<Task, 'title' | 'dueDate' | 'tag'>) => { const createTask = async (taskData: Pick<Task, 'title' | 'due_date' | 'tag'>) => {
// Get next ID as per current logic in CreateScreen.vue
const nextId = () => tasks.value.sort((a, b) => a.id_ - b.id_).reduce((acc, task) => {
if (task.id_ === acc + 1)
return acc + 1
return acc
}, 0) + 1
isLoading.value = true isLoading.value = true
error.value = null error.value = null
try { try {
const newTask: Omit<Task, 'id'> = { await api.post(endpoint, { ...taskData }).then(res => res.json())
id_: nextId(), await fetchTasks()
status: TaskStatus.WAIT,
logs: [],
lastaction: Date.now(),
archived: false,
...taskData,
}
const data = await api.put('e5880167-9322-4d7b-8a38-e06bae8a7734/list', { tasks: [newTask] }).then(res => res.json())
if (data.tasks) {
tasks.value = data.tasks
}
} }
catch (e: any) { catch (e: any) {
error.value = e.message || 'Failed to create task' error.value = e.message || 'Failed to create task'
@@ -63,15 +46,29 @@ export function useTasks() {
} }
} }
const updateTask = async (task: Task | Task[]) => { const updateTask = async (task: Task) => {
isLoading.value = true isLoading.value = true
error.value = null error.value = null
const tasksToUpdate = (Array.isArray(task) ? task : [task]).map(t => ({ ...t, lastaction: Date.now() } as Task))
try { try {
const data = await api.put('e5880167-9322-4d7b-8a38-e06bae8a7734/list', { tasks: tasksToUpdate }).then(res => res.json()) await api.patch(`${endpoint}/${task.id}`, task).then(res => res.json())
if (data.tasks) { await fetchTasks()
tasks.value = data.tasks
} }
catch (e: any) {
error.value = e.message || 'Failed to update task'
console.error('Error updating task:', e)
throw e
}
finally {
isLoading.value = false
}
}
const updateTasks = async (data: Partial<Task>, keys: Task['id'][]) => {
isLoading.value = true
error.value = null
try {
await api.patch(`${endpoint}`, { data, keys }).then(res => res.json())
await fetchTasks()
} }
catch (e: any) { catch (e: any) {
error.value = e.message || 'Failed to update task' error.value = e.message || 'Failed to update task'
@@ -92,6 +89,7 @@ export function useTasks() {
fetchTasks, fetchTasks,
createTask, createTask,
updateTask, updateTask,
updateTasks,
categories, categories,
} }
} }

View File

@@ -1,24 +1,23 @@
export enum TaskStatus { export enum TaskStatus {
NONE, ARCHIVE = 'archive',
DONE, DONE = 'done',
WIP, WIP = 'wip',
WAIT, WAIT = 'wait',
FLAG, FLAG = 'flag',
} }
export interface Worklog { export interface Worklog {
start: number start: string
end: number end: number
} }
export interface Task { export interface Task {
archived: boolean
tag: string tag: string
title: string title: string
status: TaskStatus status: TaskStatus
lastaction: number | null lastaction: string | null
logs: Worklog[] logs: Worklog[] | null
dueDate: number | null due_date: string | null
id_: number id_: number
id: number id: number
} }