Schedule posts to Bluesky with Cloudflare workers.
skyscheduler.work
cf
tool
bsky-tool
cloudflare
bluesky
schedule
bsky
service
social-media
cloudflare-workers
1import { relations, sql } from "drizzle-orm";
2import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
3
4export const users = sqliteTable("users", {
5 id: text("id").primaryKey(),
6 name: text("name").notNull(),
7 email: text("email").notNull().unique(),
8 emailVerified: integer("email_verified", { mode: "boolean" })
9 .$defaultFn(() => false)
10 .notNull(),
11 image: text("image"),
12 createdAt: integer("created_at", { mode: "timestamp" })
13 .$defaultFn(() => /* @__PURE__ */ new Date())
14 .notNull(),
15 updatedAt: integer("updated_at", { mode: "timestamp" })
16 .$defaultFn(() => /* @__PURE__ */ new Date())
17 .notNull(),
18 username: text("username").unique(),
19 displayUsername: text("display_username"),
20 bskyAppPass: text("bsky_app_pass").notNull(),
21 pds: text("pds").default("https://bsky.social").notNull(),
22});
23
24export const sessions = sqliteTable(
25 "sessions",
26 {
27 id: text("id").primaryKey(),
28 expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
29 token: text("token").notNull().unique(),
30 createdAt: integer("created_at", { mode: "timestamp_ms" })
31 .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
32 .notNull(),
33 updatedAt: integer("updated_at", { mode: "timestamp_ms" })
34 .$onUpdate(() => /* @__PURE__ */ new Date())
35 .notNull(),
36 ipAddress: text("ip_address"),
37 userAgent: text("user_agent"),
38 userId: text("user_id")
39 .notNull()
40 .references(() => users.id, { onDelete: "cascade" }),
41 },
42 (table) => [index("sessions_userId_idx").on(table.userId)],
43);
44
45export const accounts = sqliteTable(
46 "accounts",
47 {
48 id: text("id").primaryKey(),
49 accountId: text("account_id").notNull(),
50 providerId: text("provider_id").notNull(),
51 userId: text("user_id")
52 .notNull()
53 .references(() => users.id, { onDelete: "cascade" }),
54 accessToken: text("access_token"),
55 refreshToken: text("refresh_token"),
56 idToken: text("id_token"),
57 accessTokenExpiresAt: integer("access_token_expires_at", {
58 mode: "timestamp_ms",
59 }),
60 refreshTokenExpiresAt: integer("refresh_token_expires_at", {
61 mode: "timestamp_ms",
62 }),
63 scope: text("scope"),
64 password: text("password"),
65 createdAt: integer("created_at", { mode: "timestamp_ms" })
66 .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
67 .notNull(),
68 updatedAt: integer("updated_at", { mode: "timestamp_ms" })
69 .$onUpdate(() => /* @__PURE__ */ new Date())
70 .notNull(),
71 },
72 (table) => [index("accounts_userId_idx").on(table.userId)],
73);
74
75export const verifications = sqliteTable(
76 "verifications",
77 {
78 id: text("id").primaryKey(),
79 identifier: text("identifier").notNull(),
80 value: text("value").notNull(),
81 expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
82 createdAt: integer("created_at", { mode: "timestamp_ms" })
83 .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
84 .notNull(),
85 updatedAt: integer("updated_at", { mode: "timestamp_ms" })
86 .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
87 .$onUpdate(() => /* @__PURE__ */ new Date())
88 .notNull(),
89 },
90 (table) => [index("verifications_identifier_idx").on(table.identifier)],
91);
92
93export const usersRelations = relations(users, ({ many }) => ({
94 sessions: many(sessions),
95 accounts: many(accounts),
96}));
97
98export const sessionsRelations = relations(sessions, ({ one }) => ({
99 users: one(users, {
100 fields: [sessions.userId],
101 references: [users.id],
102 }),
103}));
104
105export const accountsRelations = relations(accounts, ({ one }) => ({
106 users: one(users, {
107 fields: [accounts.userId],
108 references: [users.id],
109 }),
110}));