-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: settings option for cache-control header (#13)
- Loading branch information
1 parent
a9a6e4b
commit 588bfe3
Showing
14 changed files
with
289 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { getUser } from '@/utils/auth'; | ||
import { notFound } from 'next/navigation'; | ||
import { TabGroup } from '@/components'; | ||
|
||
type Props = { children: React.ReactNode }; | ||
|
||
const Layout = async ({ children }: Props): Promise<JSX.Element> => { | ||
const user = await getUser(); | ||
if (!user?.admin) return notFound(); | ||
|
||
return ( | ||
<div className="mx-4 my-4 flex flex-col gap-2"> | ||
<TabGroup | ||
tabs={[ | ||
{ label: 'General', href: '/settings', exactMatch: true }, | ||
{ label: 'Visibility', href: '/settings/visibility' }, | ||
]} | ||
/> | ||
|
||
{children} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Layout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
'use client'; | ||
|
||
import type { updateCacheHeader } from '@/utils/actions/settings'; | ||
import type { SettingsRecord } from '@/utils/db/queries'; | ||
import { useAction } from 'next-safe-action/hook'; | ||
import { useRouter } from 'next/navigation'; | ||
import { useState } from 'react'; | ||
|
||
type Props = { | ||
settings: Record<string, SettingsRecord> | undefined; | ||
updateCacheHeaderAction: typeof updateCacheHeader; | ||
}; | ||
|
||
export const SettingsGrid = ({ settings, updateCacheHeaderAction }: Props) => { | ||
const router = useRouter(); | ||
|
||
const [cacheHeader, setCacheHeader] = useState(settings?.['cache-header']?.value ?? ''); | ||
|
||
const { execute, isExecuting } = useAction(updateCacheHeaderAction, { | ||
onSuccess: () => router.refresh(), | ||
onError: ({ fetchError, serverError, validationError }, reset) => { | ||
// eslint-disable-next-line no-console | ||
console.error('Error updating cache header', fetchError, serverError, validationError); | ||
|
||
// eslint-disable-next-line no-alert | ||
alert('Error updating cache header'); | ||
|
||
reset(); | ||
}, | ||
}); | ||
|
||
return ( | ||
<div className="grid grid-cols-2"> | ||
<div className="flex flex-col"> | ||
<span className="font-semibold">Object Cache-Control Header</span> | ||
<span className="text-sm">The cache header applied to object GET response.</span> | ||
<div className="flex flex-row"> | ||
<input | ||
type="text" | ||
className="w-full max-w-sm rounded-md rounded-r-none border border-secondary px-2 py-1 focus:border-accent/60 focus:outline-none dark:border-secondary-dark dark:focus:border-accent-dark/60" | ||
value={cacheHeader} | ||
onChange={(e) => setCacheHeader(e.target.value)} | ||
/> | ||
<button | ||
type="button" | ||
className="rounded-md rounded-l-none border border-secondary px-2 py-1 focus:border-accent/60 disabled:pointer-events-none disabled:opacity-50 dark:border-secondary-dark dark:focus:border-accent-dark/60" | ||
onClick={() => execute({ cacheHeader })} | ||
disabled={isExecuting} | ||
> | ||
Save | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { q } from '@/utils/db'; | ||
import { getBucketsFromEnv } from '@/utils/cf'; | ||
import { updateVisibility } from '@/utils/actions/access-control'; | ||
import type { Metadata } from 'next'; | ||
import { Header } from '@/components'; | ||
import type { VisibilityTableRecord } from './visibility-table'; | ||
import { VisibilityTable } from './visibility-table'; | ||
|
||
export const metadata: Metadata = { | ||
title: 'Settings', | ||
}; | ||
|
||
const Page = async (): Promise<JSX.Element> => { | ||
const records = await q.getVisibilityRecords(); | ||
const buckets = getBucketsFromEnv(); | ||
|
||
const allRecords = [ | ||
...(records ?? []), | ||
...Object.keys(buckets) | ||
.filter((b) => !records?.find((r) => r.kind === 'r2' && r.key === b)) | ||
.map( | ||
(b) => | ||
({ | ||
kind: 'r2', | ||
key: b, | ||
glob: '*', | ||
public: false, | ||
readOnly: true, | ||
}) satisfies VisibilityTableRecord, | ||
), | ||
]; | ||
|
||
return ( | ||
<div className="flex flex-col gap-2"> | ||
<Header | ||
title="Visibility Settings" | ||
desc="Control the visibility of your bindings, whether they are publically accessible, and the | ||
permissions allowed when public." | ||
/> | ||
|
||
<VisibilityTable records={allRecords} updateVisibilityAction={updateVisibility} /> | ||
{allRecords.length === 0 && <span>No entries found</span>} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Page; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-- Migration number: 0003 2023-10-04T20:54:53.882Z | ||
|
||
create table "Settings" ("key" text not null primary key, "value" text not null, "updatedAt" timestamp default CURRENT_TIMESTAMP not null, "updatedBy" text not null references "User" ("id")); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import type { Kysely } from 'kysely'; | ||
import { sql } from 'kysely'; | ||
|
||
type MigrationFunction = ( | ||
db: Kysely<unknown>, | ||
addSql: (rawSql: string) => void, | ||
) => Promise<void> | void; | ||
|
||
export const up: MigrationFunction = async (db, addSql) => { | ||
addSql( | ||
db.schema | ||
.createTable('Settings') | ||
.addColumn('key', 'text', (c) => c.primaryKey().notNull()) | ||
.addColumn('value', 'text', (c) => c.notNull()) | ||
.addColumn('updatedAt', 'timestamp', (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`).notNull()) | ||
.addColumn('updatedBy', 'text', (col) => col.references('User.id').notNull()) | ||
.compile().sql, | ||
); | ||
}; | ||
|
||
export const down: MigrationFunction = async (db, addSql) => { | ||
addSql(db.schema.dropTable('Settings').compile().sql); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-- Migration number: 0004 2023-10-04T21:01:19.012Z | ||
|
||
alter table "Settings" add column "type" text not null; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { Kysely } from 'kysely'; | ||
// import { sql } from 'kysely'; | ||
|
||
type MigrationFunction = ( | ||
db: Kysely<unknown>, | ||
addSql: (rawSql: string) => void, | ||
) => Promise<void> | void; | ||
|
||
export const up: MigrationFunction = async (db, addSql) => { | ||
addSql( | ||
db.schema | ||
.alterTable('Settings') | ||
.addColumn('type', 'text', (c) => c.notNull()) | ||
.compile().sql, | ||
); | ||
}; | ||
|
||
export const down: MigrationFunction = async (db, addSql) => { | ||
addSql(db.schema.alterTable('Settings').dropColumn('type').compile().sql); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
'use server'; | ||
|
||
import 'server-only'; | ||
|
||
import { z } from 'zod'; | ||
import { actionWithSession } from './_action'; | ||
import { q } from '../db'; | ||
|
||
export const updateCacheHeader = actionWithSession( | ||
z.object({ | ||
cacheHeader: z.string(), | ||
}), | ||
async ({ cacheHeader }, ctx) => { | ||
if (!ctx.user?.admin) throw new Error('Unauthorized'); | ||
|
||
const resp = await q.updateSettingsRecord('general', 'cache-header', cacheHeader, ctx.user.id); | ||
|
||
if (!resp) throw new Error('Failed to update record'); | ||
|
||
return resp; | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,29 @@ | ||
'use client'; | ||
|
||
import { useEffect, useState } from 'react'; | ||
import { useEffect, useRef, useState } from 'react'; | ||
|
||
export const useLocalStorage = <T extends Record<string, unknown>>( | ||
key: string, | ||
defaultValue: T, | ||
) => { | ||
const [value, setValue] = useState<T>(() => ({ | ||
...defaultValue, | ||
...(JSON.parse(localStorage.getItem(key) || '{}') as Partial<T>), | ||
})); | ||
const [value, setValue] = useState<T>(defaultValue); | ||
|
||
const keyRef = useRef(key); | ||
|
||
useEffect(() => { | ||
if (value != null) localStorage.setItem(key, JSON.stringify(value)); | ||
}, [key, value]); | ||
if (typeof localStorage === 'undefined') return; | ||
|
||
setValue((prevVal) => ({ | ||
...prevVal, | ||
...(JSON.parse(localStorage?.getItem(keyRef.current) || '{}') as Partial<T>), | ||
})); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (value === null) return; | ||
|
||
localStorage.setItem(keyRef.current, JSON.stringify(value)); | ||
}, [value]); | ||
|
||
return [value, setValue] as const; | ||
}; |