1
0

Added 'created' attribute to files and pages

This commit is contained in:
Zef Hemel 2023-11-03 09:38:04 +01:00
parent 3f1aa45b5d
commit 509683c537
16 changed files with 39 additions and 2 deletions

View File

@ -48,6 +48,7 @@ export class DataStoreSpacePrimitives implements SpacePrimitives {
): Promise<FileMeta> { ): Promise<FileMeta> {
const meta: FileMeta = { const meta: FileMeta = {
name, name,
created: suggestedMeta?.lastModified || Date.now(),
lastModified: suggestedMeta?.lastModified || Date.now(), lastModified: suggestedMeta?.lastModified || Date.now(),
contentType: mime.getType(name) || "application/octet-stream", contentType: mime.getType(name) || "application/octet-stream",
size: data.byteLength, size: data.byteLength,

View File

@ -57,6 +57,7 @@ export class DenoKVSpacePrimitives implements SpacePrimitives {
): Promise<FileMeta> { ): Promise<FileMeta> {
const meta: FileMeta = { const meta: FileMeta = {
name, name,
created: suggestedMeta?.created || Date.now(),
lastModified: suggestedMeta?.lastModified || Date.now(), lastModified: suggestedMeta?.lastModified || Date.now(),
contentType: mime.getType(name) || "application/octet-stream", contentType: mime.getType(name) || "application/octet-stream",
size: data.byteLength, size: data.byteLength,

View File

@ -54,6 +54,7 @@ export class DiskSpacePrimitives implements SpacePrimitives {
data, data,
meta: { meta: {
name: name, name: name,
created: s.birthtime!.getTime(),
lastModified: s.mtime!.getTime(), lastModified: s.mtime!.getTime(),
perm: "rw", perm: "rw",
size: s.size, size: s.size,
@ -108,6 +109,7 @@ export class DiskSpacePrimitives implements SpacePrimitives {
name: name, name: name,
size: s.size, size: s.size,
contentType: lookupContentType(name), contentType: lookupContentType(name),
created: s.birthtime!.getTime(),
lastModified: s.mtime!.getTime(), lastModified: s.mtime!.getTime(),
perm: "rw", perm: "rw",
}; };
@ -145,6 +147,7 @@ export class DiskSpacePrimitives implements SpacePrimitives {
} }
allFiles.push({ allFiles.push({
name: normalizeForwardSlashPath(name), name: normalizeForwardSlashPath(name),
created: s.birthtime!.getTime(),
lastModified: s.mtime!.getTime(), lastModified: s.mtime!.getTime(),
contentType: mime.getType(fullPath) || "application/octet-stream", contentType: mime.getType(fullPath) || "application/octet-stream",
size: s.size, size: s.size,

View File

@ -108,6 +108,7 @@ export class HttpSpacePrimitives implements SpacePrimitives {
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
}; };
if (meta) { if (meta) {
headers["X-Created"] = "" + meta.created;
headers["X-Last-Modified"] = "" + meta.lastModified; headers["X-Last-Modified"] = "" + meta.lastModified;
headers["X-Perm"] = "" + meta.perm; headers["X-Perm"] = "" + meta.perm;
} }
@ -165,6 +166,7 @@ export class HttpSpacePrimitives implements SpacePrimitives {
? +res.headers.get("X-Content-Length")! ? +res.headers.get("X-Content-Length")!
: +res.headers.get("Content-Length")!, : +res.headers.get("Content-Length")!,
contentType: res.headers.get("Content-type")!, contentType: res.headers.get("Content-type")!,
created: +(res.headers.get("X-Created") || "0"),
lastModified: +(res.headers.get("X-Last-Modified") || "0"), lastModified: +(res.headers.get("X-Last-Modified") || "0"),
perm: (res.headers.get("X-Permission") as "rw" | "ro") || "ro", perm: (res.headers.get("X-Permission") as "rw" | "ro") || "ro",
}; };

View File

@ -1,5 +1,6 @@
export type FileMeta = { export type FileMeta = {
name: string; name: string;
created: number;
lastModified: number; lastModified: number;
contentType: string; contentType: string;
size: number; size: number;
@ -9,6 +10,7 @@ export type FileMeta = {
export type PageMeta = { export type PageMeta = {
name: string; name: string;
created: number;
lastModified: number; lastModified: number;
lastOpened?: number; lastOpened?: number;
perm: "ro" | "rw"; perm: "ro" | "rw";
@ -17,6 +19,7 @@ export type PageMeta = {
export type AttachmentMeta = { export type AttachmentMeta = {
name: string; name: string;
contentType: string; contentType: string;
created: number;
lastModified: number; lastModified: number;
size: number; size: number;
perm: "ro" | "rw"; perm: "ro" | "rw";

View File

@ -34,6 +34,7 @@ export default function fileSystemSyscalls(root = "/"): SysCallMapping {
const s = await Deno.stat(p); const s = await Deno.stat(p);
return { return {
name: filePath, name: filePath,
created: s.birthtime!.getTime(),
lastModified: s.mtime!.getTime(), lastModified: s.mtime!.getTime(),
contentType: mime.getType(filePath) || "application/octet-stream", contentType: mime.getType(filePath) || "application/octet-stream",
size: s.size, size: s.size,
@ -56,6 +57,7 @@ export default function fileSystemSyscalls(root = "/"): SysCallMapping {
const s = await Deno.stat(p); const s = await Deno.stat(p);
return { return {
name: filePath, name: filePath,
created: s.birthtime!.getTime(),
lastModified: s.mtime!.getTime(), lastModified: s.mtime!.getTime(),
contentType: mime.getType(filePath) || "application/octet-stream", contentType: mime.getType(filePath) || "application/octet-stream",
size: s.size, size: s.size,
@ -84,6 +86,7 @@ export default function fileSystemSyscalls(root = "/"): SysCallMapping {
const s = await Deno.stat(fullPath); const s = await Deno.stat(fullPath);
allFiles.push({ allFiles.push({
name: fullPath.substring(dirPath.length + 1), name: fullPath.substring(dirPath.length + 1),
created: s.birthtime!.getTime(),
lastModified: s.mtime!.getTime(), lastModified: s.mtime!.getTime(),
contentType: mime.getType(fullPath) || "application/octet-stream", contentType: mime.getType(fullPath) || "application/octet-stream",
size: s.size, size: s.size,

View File

@ -25,6 +25,7 @@ async function responseToFileMeta(
: 0, : 0,
contentType: r.headers.get("Content-type")!, contentType: r.headers.get("Content-type")!,
perm, perm,
created: +(r.headers.get("X-Created") || "0"),
lastModified: +(r.headers.get("X-Last-Modified") || "0"), lastModified: +(r.headers.get("X-Last-Modified") || "0"),
}; };
} }
@ -147,6 +148,7 @@ function errorResult(
meta: { meta: {
name, name,
contentType: "text/markdown", contentType: "text/markdown",
created: 0,
lastModified: 0, lastModified: 0,
size: 0, size: 0,
perm: "ro", perm: "ro",

View File

@ -8,7 +8,8 @@ import { indexObjects } from "./api.ts";
export type PageObject = ObjectValue< export type PageObject = ObjectValue<
// The base is PageMeta, but we override lastModified to be a string // The base is PageMeta, but we override lastModified to be a string
Omit<PageMeta, "lastModified"> & { Omit<Omit<PageMeta, "lastModified">, "created"> & {
created: string; // indexing it as a string
lastModified: string; // indexing it as a string lastModified: string; // indexing it as a string
} & Record<string, any> } & Record<string, any>
>; >;
@ -23,6 +24,7 @@ export async function indexPage({ name, tree }: IndexTreeEvent) {
ref: name, ref: name,
tags: [], // will be overridden in a bit tags: [], // will be overridden in a bit
...pageMeta, ...pageMeta,
created: new Date(pageMeta.created).toISOString(),
lastModified: new Date(pageMeta.lastModified).toISOString(), lastModified: new Date(pageMeta.lastModified).toISOString(),
}; };

View File

@ -73,6 +73,7 @@ export async function readFileSearch(
name, name,
contentType: "text/markdown", contentType: "text/markdown",
size: text.length, size: text.length,
created: 0,
lastModified: 0, lastModified: 0,
perm: "ro", perm: "ro",
}, },
@ -91,6 +92,7 @@ export function getFileMetaSearch(name: string): FileMeta {
name, name,
contentType: "text/markdown", contentType: "text/markdown",
size: -1, size: -1,
created: 0,
lastModified: 0, lastModified: 0,
perm: "ro", perm: "ro",
}; };

View File

@ -40,6 +40,7 @@ export async function instantiateTemplateCommand() {
const tempPageMeta: PageMeta = { const tempPageMeta: PageMeta = {
name: "", name: "",
created: 0,
lastModified: 0, lastModified: 0,
perm: "rw", perm: "rw",
}; };
@ -207,6 +208,7 @@ export async function dailyNoteCommand() {
pageName, pageName,
await replaceTemplateVars(dailyNoteTemplateText, { await replaceTemplateVars(dailyNoteTemplateText, {
name: pageName, name: pageName,
created: 0,
lastModified: 0, lastModified: 0,
perm: "rw", perm: "rw",
}), }),
@ -251,6 +253,7 @@ export async function weeklyNoteCommand() {
pageName, pageName,
await replaceTemplateVars(weeklyNoteTemplateText, { await replaceTemplateVars(weeklyNoteTemplateText, {
name: pageName, name: pageName,
created: 0,
lastModified: 0, lastModified: 0,
perm: "rw", perm: "rw",
}), }),
@ -272,7 +275,8 @@ export async function insertTemplateText(cmdDef: any) {
// Likely page not yet created // Likely page not yet created
pageMeta = { pageMeta = {
name: page, name: page,
lastModified: -1, created: 0,
lastModified: 0,
perm: "rw", perm: "rw",
}; };
} }

View File

@ -23,6 +23,7 @@ for await (
allFiles.push({ allFiles.push({
name: fullPath.substring(rootDir.length + 1), name: fullPath.substring(rootDir.length + 1),
lastModified: lastModifiedTimestamp, lastModified: lastModifiedTimestamp,
created: lastModifiedTimestamp,
contentType: mime.getType(fullPath) || "application/octet-stream", contentType: mime.getType(fullPath) || "application/octet-stream",
size: s.size, size: s.size,
perm: "rw", perm: "rw",

View File

@ -540,6 +540,10 @@ export class HttpServer {
"X-Last-Modified", "X-Last-Modified",
"" + fileMeta.lastModified, "" + fileMeta.lastModified,
); );
headers.set(
"X-Created",
"" + fileMeta.created,
);
headers.set("Cache-Control", "no-cache"); headers.set("Cache-Control", "no-cache");
headers.set("X-Permission", fileMeta.perm); headers.set("X-Permission", fileMeta.perm);
headers.set("X-Content-Length", "" + fileMeta.size); headers.set("X-Content-Length", "" + fileMeta.size);

View File

@ -5,6 +5,8 @@ import { SpacePrimitives } from "../../common/spaces/space_primitives.ts";
import { mime } from "../deps.ts"; import { mime } from "../deps.ts";
import { FileMeta } from "$sb/types.ts"; import { FileMeta } from "$sb/types.ts";
// TODO: IMPORTANT: This needs a different way to keep meta data (last modified and created dates)
export class S3SpacePrimitives implements SpacePrimitives { export class S3SpacePrimitives implements SpacePrimitives {
client: S3Client; client: S3Client;
constructor(options: ClientOptions) { constructor(options: ClientOptions) {
@ -27,6 +29,7 @@ export class S3SpacePrimitives implements SpacePrimitives {
allFiles.push({ allFiles.push({
name: this.decodePath(obj.key), name: this.decodePath(obj.key),
perm: "rw", perm: "rw",
created: 0,
lastModified: obj.lastModified.getTime(), lastModified: obj.lastModified.getTime(),
contentType: mime.getType(obj.key) || "application/octet-stream", contentType: mime.getType(obj.key) || "application/octet-stream",
size: obj.size, size: obj.size,
@ -46,6 +49,7 @@ export class S3SpacePrimitives implements SpacePrimitives {
const meta: FileMeta = { const meta: FileMeta = {
name, name,
perm: "rw", perm: "rw",
created: 0,
lastModified: new Date(obj.headers.get("Last-Modified")!).getTime(), lastModified: new Date(obj.headers.get("Last-Modified")!).getTime(),
contentType, contentType,
size: parseInt(obj.headers.get("Content-Length")!), size: parseInt(obj.headers.get("Content-Length")!),
@ -70,6 +74,8 @@ export class S3SpacePrimitives implements SpacePrimitives {
return { return {
name, name,
perm: "rw", perm: "rw",
// TODO: Created is not accurate
created: 0,
lastModified: new Date(stat.lastModified).getTime(), lastModified: new Date(stat.lastModified).getTime(),
size: stat.size, size: stat.size,
contentType: mime.getType(name) || "application/octet-stream", contentType: mime.getType(name) || "application/octet-stream",

View File

@ -138,6 +138,7 @@ async function handleLocalFileRequest(
"Content-type": data.meta.contentType, "Content-type": data.meta.contentType,
"Content-Length": "" + data.meta.size, "Content-Length": "" + data.meta.size,
"X-Permission": data.meta.perm, "X-Permission": data.meta.perm,
"X-Created": "" + data.meta.created,
"X-Last-Modified": "" + data.meta.lastModified, "X-Last-Modified": "" + data.meta.lastModified,
}, },
}, },

View File

@ -7,6 +7,7 @@ The API:
* `GET /index.json` will return a full listing of all files in your space including metadata like when the file was last modified, as well as permissions. This is primarily used for sync purposes with the client. * `GET /index.json` will return a full listing of all files in your space including metadata like when the file was last modified, as well as permissions. This is primarily used for sync purposes with the client.
* `GET /*.*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /index.md` you will receive the content of your `index` page. If the the optional `X-Get-Meta` _request header_ is set, the server does not _need to_ return the body of the file (but it can). The `GET` _response_ will have a few additional SB-specific headers: * `GET /*.*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /index.md` you will receive the content of your `index` page. If the the optional `X-Get-Meta` _request header_ is set, the server does not _need to_ return the body of the file (but it can). The `GET` _response_ will have a few additional SB-specific headers:
* (optional) `X-Last-Modified` the last modified time of the file as a UNIX timestamp in ms since the epoch (as coming from `Data.now()`). This timestamp _has_ to match the `lastModified` listed for this file in `/index.json` otherwise syncing issues may occur. When this header is missing, frequent polling-based sync will be disabled for this file. * (optional) `X-Last-Modified` the last modified time of the file as a UNIX timestamp in ms since the epoch (as coming from `Data.now()`). This timestamp _has_ to match the `lastModified` listed for this file in `/index.json` otherwise syncing issues may occur. When this header is missing, frequent polling-based sync will be disabled for this file.
* (optional) `X-Created` the created time of the file as a UNIX timestamp in ms since the epoch (as coming from `Data.now()`).
* (optional) `X-Permission`: either `rw` or `ro` which will change whether the editor opens in read-only or edit mode. When missing, `ro` is assumed. * (optional) `X-Permission`: either `rw` or `ro` which will change whether the editor opens in read-only or edit mode. When missing, `ro` is assumed.
* (optional) `X-Content-Length`: which will be the same as `Content-Length` except if the request was sent with a `X-Get-Meta` header and the body is not returned (then `Content-Length` will be `0` and `X-Content-Length` will be the size of the file) * (optional) `X-Content-Length`: which will be the same as `Content-Length` except if the request was sent with a `X-Get-Meta` header and the body is not returned (then `Content-Length` will be `0` and `X-Content-Length` will be the size of the file)
* `PUT /*.*`: The same as `GET` except that it takes the body of the request and _writes_ it to a file. * `PUT /*.*`: The same as `GET` except that it takes the body of the request and _writes_ it to a file.

View File

@ -1,5 +1,6 @@
/* /*
X-Last-Modified: 12345 X-Last-Modified: 12345
X-Created: 12345
X-Permission: rw X-Permission: rw
access-control-allow-headers: * access-control-allow-headers: *
access-control-allow-methods: GET,POST,PUT,DELETE,OPTIONS,HEAD access-control-allow-methods: GET,POST,PUT,DELETE,OPTIONS,HEAD