Newtypes and broker refactor #10
@@ -11,7 +11,7 @@ import { Editor } from "@/components/editor/editor";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const noteSchema = (t: any) => z.object({
|
const noteSchema = (t: any) => z.object({
|
||||||
title: z.string().min(1, t("Title is required")).max(200, t("Title too long")),
|
title: z.string().min(0, t("Title too long")).max(200, t("Title too long")),
|
||||||
content: z.string().optional(),
|
content: z.string().optional(),
|
||||||
is_pinned: z.boolean().default(false),
|
is_pinned: z.boolean().default(false),
|
||||||
tags: z.string().optional(), // Comma separated for now
|
tags: z.string().optional(), // Comma separated for now
|
||||||
|
|||||||
45
migrations/20251231000000_nullable_title.sql
Normal file
45
migrations/20251231000000_nullable_title.sql
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
-- Allow NULL titles in notes table
|
||||||
|
-- SQLite doesn't support ALTER COLUMN, so we need to recreate the table
|
||||||
|
|
||||||
|
-- Step 1: Create new table with nullable title
|
||||||
|
CREATE TABLE notes_new (
|
||||||
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
title TEXT, -- Now nullable
|
||||||
|
content TEXT NOT NULL DEFAULT '',
|
||||||
|
color TEXT NOT NULL DEFAULT 'DEFAULT',
|
||||||
|
is_pinned INTEGER NOT NULL DEFAULT 0,
|
||||||
|
is_archived INTEGER NOT NULL DEFAULT 0,
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Step 2: Copy data from old table
|
||||||
|
INSERT INTO notes_new (id, user_id, title, content, color, is_pinned, is_archived, created_at, updated_at)
|
||||||
|
SELECT id, user_id, title, content, color, is_pinned, is_archived, created_at, updated_at FROM notes;
|
||||||
|
|
||||||
|
-- Step 3: Drop old table
|
||||||
|
DROP TABLE notes;
|
||||||
|
|
||||||
|
-- Step 4: Rename new table
|
||||||
|
ALTER TABLE notes_new RENAME TO notes;
|
||||||
|
|
||||||
|
-- Step 5: Recreate indexes
|
||||||
|
CREATE INDEX idx_notes_user_id ON notes(user_id);
|
||||||
|
CREATE INDEX idx_notes_is_pinned ON notes(is_pinned);
|
||||||
|
CREATE INDEX idx_notes_is_archived ON notes(is_archived);
|
||||||
|
CREATE INDEX idx_notes_updated_at ON notes(updated_at);
|
||||||
|
|
||||||
|
-- Step 6: Recreate FTS triggers
|
||||||
|
CREATE TRIGGER notes_ai AFTER INSERT ON notes BEGIN
|
||||||
|
INSERT INTO notes_fts(rowid, title, content) VALUES (NEW.rowid, COALESCE(NEW.title, ''), NEW.content);
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER notes_ad AFTER DELETE ON notes BEGIN
|
||||||
|
INSERT INTO notes_fts(notes_fts, rowid, title, content) VALUES('delete', OLD.rowid, COALESCE(OLD.title, ''), OLD.content);
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER notes_au AFTER UPDATE ON notes BEGIN
|
||||||
|
INSERT INTO notes_fts(notes_fts, rowid, title, content) VALUES('delete', OLD.rowid, COALESCE(OLD.title, ''), OLD.content);
|
||||||
|
INSERT INTO notes_fts(rowid, title, content) VALUES (NEW.rowid, COALESCE(NEW.title, ''), NEW.content);
|
||||||
|
END;
|
||||||
@@ -10,7 +10,7 @@ use notes_domain::{Note, Tag};
|
|||||||
/// Request to create a new note
|
/// Request to create a new note
|
||||||
#[derive(Debug, Deserialize, Validate)]
|
#[derive(Debug, Deserialize, Validate)]
|
||||||
pub struct CreateNoteRequest {
|
pub struct CreateNoteRequest {
|
||||||
#[validate(length(min = 1, max = 200, message = "Title must be 1-200 characters"))]
|
#[validate(length(max = 200, message = "Title must be at most 200 characters"))]
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -29,7 +29,7 @@ pub struct CreateNoteRequest {
|
|||||||
/// Request to update an existing note (all fields optional)
|
/// Request to update an existing note (all fields optional)
|
||||||
#[derive(Debug, Deserialize, Validate)]
|
#[derive(Debug, Deserialize, Validate)]
|
||||||
pub struct UpdateNoteRequest {
|
pub struct UpdateNoteRequest {
|
||||||
#[validate(length(min = 1, max = 200, message = "Title must be 1-200 characters"))]
|
#[validate(length(max = 200, message = "Title must be at most 200 characters"))]
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
|
|
||||||
pub content: Option<String>,
|
pub content: Option<String>,
|
||||||
|
|||||||
Reference in New Issue
Block a user