all in a days work
This commit is contained in:
parent
64b9c16eed
commit
5e178a6a19
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ node_modules
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
devdocker
|
||||
|
16
drizzle.config.ts
Normal file
16
drizzle.config.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { defineConfig } from 'drizzle-kit';
|
||||
|
||||
export default defineConfig({
|
||||
dialect: 'mysql',
|
||||
schema: './src/lib/server/drizzle/schema.ts',
|
||||
out: './src/lib/server/drizzle/migrations',
|
||||
dbCredentials: {
|
||||
host: process.env.DATABASE_HOST as string,
|
||||
port: Number(process.env.DATABASE_PORT) || 3306,
|
||||
database: process.env.DATABASE_DB as string,
|
||||
user: process.env.DATABASE_USER as string,
|
||||
password: process.env.DATABASE_PASS as string
|
||||
},
|
||||
verbose: true,
|
||||
strict: true
|
||||
})
|
1564
package-lock.json
generated
1564
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -15,9 +15,13 @@
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/eslint": "^8.56.0",
|
||||
"@types/node": "^20.12.12",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"drizzle-kit": "^0.21.2",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
@ -31,6 +35,13 @@
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@sveltejs/adapter-node": "^5.0.1"
|
||||
"@sveltejs/adapter-node": "^5.0.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"drizzle-orm": "^0.30.10",
|
||||
"mysql2": "^3.9.7",
|
||||
"otplib": "^12.0.1",
|
||||
"svelte-kit-cookie-session": "^4.0.0",
|
||||
"sveltekit-i18n": "^2.4.2",
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
}
|
||||
|
78
src/app.css
Normal file
78
src/app.css
Normal file
@ -0,0 +1,78 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--in-text-color: #fff;
|
||||
--in-link-color: #fff;
|
||||
--in-outline-color: #00aaff;
|
||||
--in-normalized-background: #000;
|
||||
--in-input-background: #fff;
|
||||
--in-input-color: #000;
|
||||
--in-input-border-color: #ddd;
|
||||
|
||||
--in-focus-outline: 3px solid var(--in-outline-color);
|
||||
}
|
||||
|
||||
:root {
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Open Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
color: var(--in-text-color);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--in-normalized-background);
|
||||
background-image: url('/background.jpg');
|
||||
background-attachment: fixed;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--in-link-color);
|
||||
|
||||
&:visited {
|
||||
color: var(--in-link-color);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--in-focus-outline);
|
||||
}
|
||||
}
|
||||
|
||||
a[target='_blank']::after {
|
||||
content: '';
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 style=%27width:24px;height:24px%27 viewBox=%270 0 24 24%27%3E%3Cpath fill=%27%23ffffff%27 d=%27M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z%27 /%3E%3C/svg%3E');
|
||||
width: 0.95rem;
|
||||
height: 0.95rem;
|
||||
display: inline-block;
|
||||
margin-left: 2px;
|
||||
vertical-align: top;
|
||||
}
|
18
src/app.d.ts
vendored
18
src/app.d.ts
vendored
@ -1,10 +1,24 @@
|
||||
import type { UserSession } from '$lib/server/users/types';
|
||||
import type { Session } from 'svelte-kit-cookie-session';
|
||||
|
||||
type SessionData = {
|
||||
user?: UserSession;
|
||||
}
|
||||
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
|
||||
interface Locals {
|
||||
session: Session<SessionData>;
|
||||
}
|
||||
|
||||
interface PageData {
|
||||
session: SessionData;
|
||||
}
|
||||
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
|
7
src/hooks.server.ts
Normal file
7
src/hooks.server.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { SESSION_SECRET } from '$env/static/private';
|
||||
import '$lib/server/drizzle';
|
||||
import { handleSession } from 'svelte-kit-cookie-session';
|
||||
|
||||
export const handle = handleSession({
|
||||
secret: SESSION_SECRET
|
||||
})
|
36
src/lib/components/Button.svelte
Normal file
36
src/lib/components/Button.svelte
Normal file
@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
export let type: 'button' | 'submit' = 'button';
|
||||
export let variant: 'default' | 'primary' | 'link' = 'default';
|
||||
</script>
|
||||
|
||||
<button {type} class="btn btn-{variant}"><slot /></button>
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
appearance: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: var(--in-text-color);
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--in-focus-outline);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn-default,
|
||||
.btn-primary {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
border: 2px solid #ddd;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
3
src/lib/components/LogoutButton.svelte
Normal file
3
src/lib/components/LogoutButton.svelte
Normal file
@ -0,0 +1,3 @@
|
||||
<form action="/account?/logout" method="POST">
|
||||
<button type="submit" class="btn btn-link">Log out</button>
|
||||
</form>
|
33
src/lib/components/form/FormControl.svelte
Normal file
33
src/lib/components/form/FormControl.svelte
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
<div class="form-control">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(input) {
|
||||
&:not([type]),
|
||||
&[type=text],
|
||||
&[type=password],
|
||||
&[type=email] {
|
||||
padding: 8px;
|
||||
font-size: 1rem;
|
||||
background-color: var(--in-input-background);
|
||||
color: var(--in-input-color);
|
||||
border: 2px solid var(--in-input-border-color);
|
||||
border-radius: 6px;
|
||||
|
||||
&:focus-visible {
|
||||
outline: var(--in-focus-outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(label) {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
4
src/lib/components/form/FormSection.svelte
Normal file
4
src/lib/components/form/FormSection.svelte
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
<div class="form-section">
|
||||
<slot />
|
||||
</div>
|
10
src/lib/components/form/FormWrapper.svelte
Normal file
10
src/lib/components/form/FormWrapper.svelte
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
<div class="form-wrapper">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(.form-control) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
28
src/lib/i18n/en/account.json
Normal file
28
src/lib/i18n/en/account.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"username": "Username",
|
||||
"displayName": "Display Name",
|
||||
"changeEmail": "Change email address",
|
||||
"currentEmail": "Current email address",
|
||||
"newEmail": "New email address",
|
||||
"changePassword": "Change password",
|
||||
"currentPassword": "Current password",
|
||||
"newPassword": "New password",
|
||||
"repeatPassword": "Repeat new password",
|
||||
"submit": "Submit",
|
||||
"login": {
|
||||
"title": "Log in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Log in"
|
||||
},
|
||||
"errors": {
|
||||
"invalidLogin": "Invalid email or password!",
|
||||
"invalidRequest": "Invalid request! Please try again.",
|
||||
"emailRequired": "Email address is required.",
|
||||
"invalidEmail": "The email address is invalid.",
|
||||
"passwordRequired": "The password is required.",
|
||||
"passwordMismatch": "The passwords do not match!",
|
||||
"invalidPassword": "The provided password is invalid.",
|
||||
"invalidDisplayName": "The provided display name is invalid."
|
||||
}
|
||||
}
|
5
src/lib/i18n/en/common.json
Normal file
5
src/lib/i18n/en/common.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"siteName": "Icy Network",
|
||||
"description": "Icy Network is a Single-Sign-On service used by other applications.",
|
||||
"cookieDisclaimer": "The website may use temporary cookies for storing your login session and ensuring your security. This web service is <a href=\"https://git.icynet.eu/IcyNetwork/icynet-auth-server\" target=\"_blank\">completely open source</a> and can be audited by anyone."
|
||||
}
|
18
src/lib/i18n/index.ts
Normal file
18
src/lib/i18n/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import i18n from 'sveltekit-i18n';
|
||||
|
||||
const config = {
|
||||
loaders: [
|
||||
{
|
||||
locale: 'en',
|
||||
key: 'common',
|
||||
loader: async () => await import('./en/common.json')
|
||||
},
|
||||
{
|
||||
locale: 'en',
|
||||
key: 'account',
|
||||
loader: async () => await import('./en/account.json')
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const { t, locale, locales, loading, loadTranslations } = new i18n(config);
|
79
src/lib/server/challenge.ts
Normal file
79
src/lib/server/challenge.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { Changesets } from './changesets';
|
||||
import { CryptoUtils } from './crypto-utils';
|
||||
import type { User } from './drizzle';
|
||||
import { TimeOTP } from './users/totp';
|
||||
|
||||
export interface ChallengeBody<T> {
|
||||
aud: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export class Challenge {
|
||||
static async issueChallenge<TChallenge>(
|
||||
challenge: TChallenge,
|
||||
recipient: string
|
||||
): Promise<string> {
|
||||
const body = <ChallengeBody<TChallenge>>{
|
||||
aud: recipient,
|
||||
data: challenge
|
||||
};
|
||||
return CryptoUtils.encryptChallenge(body);
|
||||
}
|
||||
|
||||
static async verifyChallenge<TRes>(
|
||||
challenge: string,
|
||||
recipient: string,
|
||||
code: string,
|
||||
secret: string
|
||||
) {
|
||||
const { aud, data }: ChallengeBody<TRes> = await CryptoUtils.decryptChallenge(challenge);
|
||||
if (aud !== recipient) {
|
||||
throw new Error('Invalid challenge');
|
||||
}
|
||||
|
||||
if (!TimeOTP.validate(secret, code)) {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static async challengeFromBody<TRes>(
|
||||
body: FormData,
|
||||
recipient: string,
|
||||
secret: string
|
||||
): Promise<TRes | undefined> {
|
||||
const { challenge, otpCode } = Changesets.take<{ challenge?: string; otpCode?: string }>(
|
||||
['challenge', 'otpCode'],
|
||||
body
|
||||
);
|
||||
|
||||
if (!challenge || !otpCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Challenge.verifyChallenge<TRes>(challenge, recipient, otpCode, secret);
|
||||
}
|
||||
|
||||
static async authorizedChanges<TRes>(
|
||||
fields: (keyof TRes)[],
|
||||
body: FormData,
|
||||
subject: User
|
||||
): Promise<Partial<TRes>> {
|
||||
if (!body.has('challenge')) {
|
||||
return Changesets.take<TRes>(fields, body);
|
||||
}
|
||||
|
||||
const userOtp = await TimeOTP.getUserOtp(subject);
|
||||
if (!userOtp) {
|
||||
throw new Error('Invalid request');
|
||||
}
|
||||
|
||||
const data = await Challenge.challengeFromBody<TRes>(body, subject.uuid, userOtp.token);
|
||||
if (!data) {
|
||||
throw new Error('Invalid request');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
14
src/lib/server/changesets.ts
Normal file
14
src/lib/server/changesets.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export class Changesets {
|
||||
static take<TRes>(
|
||||
fields: (keyof TRes)[],
|
||||
body: FormData,
|
||||
challenge?: Partial<TRes>
|
||||
): Partial<TRes> {
|
||||
return fields.reduce<Partial<TRes>>((accum, field) => {
|
||||
accum[field] = challenge
|
||||
? challenge[field]
|
||||
: ((body.get(field as string) as string)?.trim() as TRes[typeof field]);
|
||||
return accum;
|
||||
}, {});
|
||||
}
|
||||
}
|
63
src/lib/server/crypto-utils.ts
Normal file
63
src/lib/server/crypto-utils.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { CHALLENGE_SECRET } from '$env/static/private';
|
||||
import * as crypto from 'crypto';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
const IV_LENGTH = 16;
|
||||
const ALGORITHM = 'aes-256-cbc';
|
||||
|
||||
export class CryptoUtils {
|
||||
public static generateString(length: number): string {
|
||||
return crypto.randomBytes(length).toString('hex').slice(0, length);
|
||||
}
|
||||
|
||||
public static generateSecret(): string {
|
||||
return crypto.randomBytes(256 / 8).toString('hex');
|
||||
}
|
||||
|
||||
public static insecureHash(input: string): string {
|
||||
return crypto.createHash('md5').update(input).digest('hex');
|
||||
}
|
||||
|
||||
public static createUUID(): string {
|
||||
return v4();
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/q/52212430
|
||||
/**
|
||||
* Symmetric encryption function
|
||||
* @param text String to encrypt
|
||||
* @param key Encryption key
|
||||
* @returns Encrypted text
|
||||
*/
|
||||
public static encrypt(text: string, key: string): string {
|
||||
const iv = crypto.randomBytes(IV_LENGTH);
|
||||
const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(key, 'hex'), iv);
|
||||
let encrypted = cipher.update(text);
|
||||
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
||||
return `${iv.toString('hex')}:${encrypted.toString('hex')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric decryption function
|
||||
* @param text Encrypted string
|
||||
* @param key Decryption key
|
||||
* @returns Decrypted text
|
||||
*/
|
||||
public static decrypt(text: string, key: string): string {
|
||||
const [iv, encryptedText] = text.split(':').map((part) => Buffer.from(part, 'hex'));
|
||||
|
||||
const decipher = crypto.createDecipheriv(ALGORITHM, Buffer.from(key, 'hex'), iv);
|
||||
|
||||
let decrypted = decipher.update(encryptedText);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
return decrypted.toString();
|
||||
}
|
||||
|
||||
public static async encryptChallenge<T>(challenge: T): Promise<string> {
|
||||
return this.encrypt(JSON.stringify(challenge), CHALLENGE_SECRET);
|
||||
}
|
||||
|
||||
public static async decryptChallenge<T>(challenge: string): Promise<T> {
|
||||
return JSON.parse(this.decrypt(challenge, CHALLENGE_SECRET));
|
||||
}
|
||||
}
|
14
src/lib/server/drizzle/index.ts
Normal file
14
src/lib/server/drizzle/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { DATABASE_DB, DATABASE_HOST, DATABASE_PASS } from '$env/static/private';
|
||||
import { drizzle } from 'drizzle-orm/mysql2';
|
||||
import mysql from 'mysql2/promise';
|
||||
import * as schema from './schema';
|
||||
|
||||
const connection = await mysql.createConnection({
|
||||
host: DATABASE_HOST,
|
||||
user: DATABASE_PASS,
|
||||
password: DATABASE_PASS,
|
||||
database: DATABASE_DB
|
||||
});
|
||||
|
||||
export const db = drizzle(connection, { schema, mode: 'default' });
|
||||
export * from './schema';
|
137
src/lib/server/drizzle/migrations/0000_initial.sql
Normal file
137
src/lib/server/drizzle/migrations/0000_initial.sql
Normal file
@ -0,0 +1,137 @@
|
||||
-- Current sql file was generated after introspecting the database
|
||||
-- If you want to run this migration please uncomment this code before executing migrations
|
||||
/*
|
||||
CREATE TABLE `audit_log` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`action` text NOT NULL,
|
||||
`content` text DEFAULT 'NULL',
|
||||
`actor_ip` text DEFAULT 'NULL',
|
||||
`actor_ua` text DEFAULT 'NULL',
|
||||
`flagged` tinyint NOT NULL DEFAULT 0,
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`actorId` int(11) DEFAULT 'NULL'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `document` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`title` text NOT NULL,
|
||||
`slug` text NOT NULL,
|
||||
`body` text NOT NULL,
|
||||
`authorId` int(11) DEFAULT 'NULL',
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`updated_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `o_auth2_client` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`client_id` varchar(36) NOT NULL,
|
||||
`client_secret` text NOT NULL,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`description` text DEFAULT 'NULL',
|
||||
`scope` text DEFAULT 'NULL',
|
||||
`grants` text NOT NULL DEFAULT ''authorization_code'',
|
||||
`activated` tinyint NOT NULL DEFAULT 0,
|
||||
`verified` tinyint NOT NULL DEFAULT 0,
|
||||
`pictureId` int(11) DEFAULT 'NULL',
|
||||
`ownerId` int(11) DEFAULT 'NULL',
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`updated_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
CONSTRAINT `IDX_e9d16c213910ad57bd05e97b42` UNIQUE(`client_id`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `o_auth2_client_authorization` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`scope` text DEFAULT 'NULL',
|
||||
`expires_at` timestamp NOT NULL DEFAULT 'current_timestamp()',
|
||||
`clientId` int(11) DEFAULT 'NULL',
|
||||
`userId` int(11) DEFAULT 'NULL',
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `o_auth2_client_url` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`url` varchar(255) NOT NULL,
|
||||
`type` enum('redirect_uri','terms','privacy','website') NOT NULL,
|
||||
`created_at` timestamp(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`updated_at` timestamp(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`clientId` int(11) DEFAULT 'NULL'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `o_auth2_token` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`type` enum('code','access_token','refresh_token') NOT NULL,
|
||||
`token` text NOT NULL,
|
||||
`scope` text DEFAULT 'NULL',
|
||||
`expires_at` timestamp NOT NULL DEFAULT 'current_timestamp()',
|
||||
`userId` int(11) DEFAULT 'NULL',
|
||||
`clientId` int(11) DEFAULT 'NULL',
|
||||
`nonce` text DEFAULT 'NULL',
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`updated_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`pcke` text DEFAULT 'NULL'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `privilege` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`name` text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `upload` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`original_name` varchar(255) NOT NULL,
|
||||
`mimetype` varchar(255) NOT NULL,
|
||||
`file` varchar(255) NOT NULL,
|
||||
`uploaderId` int(11) DEFAULT 'NULL',
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`updated_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `user` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`uuid` varchar(36) NOT NULL,
|
||||
`username` varchar(26) NOT NULL,
|
||||
`email` varchar(255) NOT NULL,
|
||||
`display_name` varchar(32) NOT NULL,
|
||||
`password` text DEFAULT 'NULL',
|
||||
`activated` tinyint NOT NULL DEFAULT 0,
|
||||
`activity_at` timestamp NOT NULL DEFAULT 'current_timestamp()',
|
||||
`pictureId` int(11) DEFAULT 'NULL',
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
`updated_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)',
|
||||
CONSTRAINT `IDX_a95e949168be7b7ece1a2382fe` UNIQUE(`uuid`),
|
||||
CONSTRAINT `IDX_78a916df40e02a9deb1c4b75ed` UNIQUE(`username`),
|
||||
CONSTRAINT `IDX_e12875dfb3b1d92d7d7c5377e2` UNIQUE(`email`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `user_privileges_privilege` (
|
||||
`userId` int(11) NOT NULL,
|
||||
`privilegeId` int(11) NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `user_token` (
|
||||
`id` int(11) AUTO_INCREMENT NOT NULL,
|
||||
`token` text NOT NULL,
|
||||
`type` enum('generic','activation','deactivation','password','login','gdpr','totp','public_key','recovery') NOT NULL,
|
||||
`expires_at` timestamp DEFAULT 'NULL',
|
||||
`userId` int(11) DEFAULT 'NULL',
|
||||
`nonce` text DEFAULT 'NULL',
|
||||
`created_at` datetime(6) NOT NULL DEFAULT 'current_timestamp(6)'
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `audit_log` ADD CONSTRAINT `FK_cb6aa6f6fd56f08eafb60316225` FOREIGN KEY (`actorId`) REFERENCES `user`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `document` ADD CONSTRAINT `FK_6a2eb13cadfc503989cbe367572` FOREIGN KEY (`authorId`) REFERENCES `user`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `o_auth2_client` ADD CONSTRAINT `FK_4a6c878506b872e85b3d07f6252` FOREIGN KEY (`ownerId`) REFERENCES `user`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `o_auth2_client` ADD CONSTRAINT `FK_e8d65b1eec13474e493420517d7` FOREIGN KEY (`pictureId`) REFERENCES `upload`(`id`) ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `o_auth2_client_authorization` ADD CONSTRAINT `FK_8227110f58510b7233f3db90cfb` FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `o_auth2_client_authorization` ADD CONSTRAINT `FK_9ca9ebb654e7ce71954d5fdb281` FOREIGN KEY (`clientId`) REFERENCES `o_auth2_client`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `o_auth2_client_url` ADD CONSTRAINT `FK_aca59c7bdd65987487eea98d00f` FOREIGN KEY (`clientId`) REFERENCES `o_auth2_client`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `o_auth2_token` ADD CONSTRAINT `FK_3ecb760b321ef9bbab635f05b45` FOREIGN KEY (`clientId`) REFERENCES `o_auth2_client`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `o_auth2_token` ADD CONSTRAINT `FK_81ffb9b8d672cf3af1af9e789f3` FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE `upload` ADD CONSTRAINT `FK_7b8d52838a953b188255682597b` FOREIGN KEY (`uploaderId`) REFERENCES `user`(`id`) ON DELETE set null ON UPDATE cascade;--> statement-breakpoint
|
||||
ALTER TABLE `user` ADD CONSTRAINT `FK_7478a15985dbfa32ed5fc77a7a1` FOREIGN KEY (`pictureId`) REFERENCES `upload`(`id`) ON DELETE set null ON UPDATE cascade;--> statement-breakpoint
|
||||
ALTER TABLE `user_privileges_privilege` ADD CONSTRAINT `FK_0664a7ff494a1859a09014c0f17` FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
|
||||
ALTER TABLE `user_privileges_privilege` ADD CONSTRAINT `FK_e71171f4ed20bc8564a1819d0b7` FOREIGN KEY (`privilegeId`) REFERENCES `privilege`(`id`) ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
|
||||
ALTER TABLE `user_token` ADD CONSTRAINT `FK_d37db50eecdf9b8ce4eedd2f918` FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX `IDX_0664a7ff494a1859a09014c0f1` ON `user_privileges_privilege` (`userId`);--> statement-breakpoint
|
||||
CREATE INDEX `IDX_e71171f4ed20bc8564a1819d0b` ON `user_privileges_privilege` (`privilegeId`);
|
||||
*/
|
985
src/lib/server/drizzle/migrations/meta/0000_snapshot.json
Normal file
985
src/lib/server/drizzle/migrations/meta/0000_snapshot.json
Normal file
@ -0,0 +1,985 @@
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"prevId": "",
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"tables": {
|
||||
"audit_log": {
|
||||
"name": "audit_log",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"action": {
|
||||
"autoincrement": false,
|
||||
"name": "action",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"content": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"actor_ip": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "actor_ip",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"actor_ua": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "actor_ua",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"flagged": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "flagged",
|
||||
"type": "tinyint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"actorId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "actorId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_cb6aa6f6fd56f08eafb60316225": {
|
||||
"name": "FK_cb6aa6f6fd56f08eafb60316225",
|
||||
"tableFrom": "audit_log",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"actorId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"document": {
|
||||
"name": "document",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"autoincrement": false,
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"slug": {
|
||||
"autoincrement": false,
|
||||
"name": "slug",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"body": {
|
||||
"autoincrement": false,
|
||||
"name": "body",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"authorId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "authorId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "updated_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_6a2eb13cadfc503989cbe367572": {
|
||||
"name": "FK_6a2eb13cadfc503989cbe367572",
|
||||
"tableFrom": "document",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"authorId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"migrations": {
|
||||
"name": "migrations",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"timestamp": {
|
||||
"autoincrement": false,
|
||||
"name": "timestamp",
|
||||
"type": "bigint(20)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"autoincrement": false,
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"o_auth2_client": {
|
||||
"name": "o_auth2_client",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"client_id": {
|
||||
"autoincrement": false,
|
||||
"name": "client_id",
|
||||
"type": "varchar(36)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"client_secret": {
|
||||
"autoincrement": false,
|
||||
"name": "client_secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"autoincrement": false,
|
||||
"name": "title",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scope": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"grants": {
|
||||
"default": "''authorization_code''",
|
||||
"autoincrement": false,
|
||||
"name": "grants",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"activated": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "activated",
|
||||
"type": "tinyint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"verified": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "verified",
|
||||
"type": "tinyint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"pictureId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "pictureId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"ownerId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "ownerId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "updated_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_4a6c878506b872e85b3d07f6252": {
|
||||
"name": "FK_4a6c878506b872e85b3d07f6252",
|
||||
"tableFrom": "o_auth2_client",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"ownerId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"FK_e8d65b1eec13474e493420517d7": {
|
||||
"name": "FK_e8d65b1eec13474e493420517d7",
|
||||
"tableFrom": "o_auth2_client",
|
||||
"tableTo": "upload",
|
||||
"columnsFrom": [
|
||||
"pictureId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {
|
||||
"IDX_e9d16c213910ad57bd05e97b42": {
|
||||
"name": "IDX_e9d16c213910ad57bd05e97b42",
|
||||
"columns": [
|
||||
"client_id"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"o_auth2_client_authorization": {
|
||||
"name": "o_auth2_client_authorization",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"scope": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"default": "'current_timestamp()'",
|
||||
"autoincrement": false,
|
||||
"name": "expires_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"clientId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "clientId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"userId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "userId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_8227110f58510b7233f3db90cfb": {
|
||||
"name": "FK_8227110f58510b7233f3db90cfb",
|
||||
"tableFrom": "o_auth2_client_authorization",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"FK_9ca9ebb654e7ce71954d5fdb281": {
|
||||
"name": "FK_9ca9ebb654e7ce71954d5fdb281",
|
||||
"tableFrom": "o_auth2_client_authorization",
|
||||
"tableTo": "o_auth2_client",
|
||||
"columnsFrom": [
|
||||
"clientId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"o_auth2_client_url": {
|
||||
"name": "o_auth2_client_url",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"url": {
|
||||
"autoincrement": false,
|
||||
"name": "url",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"autoincrement": false,
|
||||
"name": "type",
|
||||
"type": "enum('redirect_uri','terms','privacy','website')",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "timestamp(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "updated_at",
|
||||
"type": "timestamp(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"clientId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "clientId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_aca59c7bdd65987487eea98d00f": {
|
||||
"name": "FK_aca59c7bdd65987487eea98d00f",
|
||||
"tableFrom": "o_auth2_client_url",
|
||||
"tableTo": "o_auth2_client",
|
||||
"columnsFrom": [
|
||||
"clientId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"o_auth2_token": {
|
||||
"name": "o_auth2_token",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"autoincrement": false,
|
||||
"name": "type",
|
||||
"type": "enum('code','access_token','refresh_token')",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"token": {
|
||||
"autoincrement": false,
|
||||
"name": "token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"scope": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"default": "'current_timestamp()'",
|
||||
"autoincrement": false,
|
||||
"name": "expires_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"userId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "userId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"clientId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "clientId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"nonce": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "nonce",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "updated_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"pcke": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "pcke",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_3ecb760b321ef9bbab635f05b45": {
|
||||
"name": "FK_3ecb760b321ef9bbab635f05b45",
|
||||
"tableFrom": "o_auth2_token",
|
||||
"tableTo": "o_auth2_client",
|
||||
"columnsFrom": [
|
||||
"clientId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"FK_81ffb9b8d672cf3af1af9e789f3": {
|
||||
"name": "FK_81ffb9b8d672cf3af1af9e789f3",
|
||||
"tableFrom": "o_auth2_token",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"privilege": {
|
||||
"name": "privilege",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"autoincrement": false,
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"upload": {
|
||||
"name": "upload",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"original_name": {
|
||||
"autoincrement": false,
|
||||
"name": "original_name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"mimetype": {
|
||||
"autoincrement": false,
|
||||
"name": "mimetype",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"file": {
|
||||
"autoincrement": false,
|
||||
"name": "file",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"uploaderId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "uploaderId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "updated_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_7b8d52838a953b188255682597b": {
|
||||
"name": "FK_7b8d52838a953b188255682597b",
|
||||
"tableFrom": "upload",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"uploaderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "cascade"
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"autoincrement": true,
|
||||
"name": "id",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"uuid": {
|
||||
"autoincrement": false,
|
||||
"name": "uuid",
|
||||
"type": "varchar(36)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"username": {
|
||||
"autoincrement": false,
|
||||
"name": "username",
|
||||
"type": "varchar(26)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"autoincrement": false,
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"display_name": {
|
||||
"autoincrement": false,
|
||||
"name": "display_name",
|
||||
"type": "varchar(32)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"password": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"activated": {
|
||||
"default": 0,
|
||||
"autoincrement": false,
|
||||
"name": "activated",
|
||||
"type": "tinyint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"activity_at": {
|
||||
"default": "'current_timestamp()'",
|
||||
"autoincrement": false,
|
||||
"name": "activity_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"pictureId": {
|
||||
"default": "'NULL'",
|
||||
"autoincrement": false,
|
||||
"name": "pictureId",
|
||||
"type": "int(11)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "created_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"default": "'current_timestamp(6)'",
|
||||
"autoincrement": false,
|
||||
"name": "updated_at",
|
||||
"type": "datetime(6)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"FK_7478a15985dbfa32ed5fc77a7a1": {
|
||||
"name": "FK_7478a15985dbfa32ed5fc77a7a1",
|
||||