Gisle Aune
3 years ago
23 changed files with 103 additions and 462 deletions
-
4api/items.go
-
4api/projects.go
-
6api/scope.go
-
4api/stats.go
-
1frontend/src/app.d.ts
-
7frontend/src/hooks.ts
-
2frontend/src/lib/components/frontpage/ScopeLink.svelte
-
17frontend/src/lib/config.ts
-
37frontend/src/lib/database/interfaces.ts
-
48frontend/src/lib/database/mysql/database.ts
-
84frontend/src/lib/database/mysql/scopes.ts
-
10frontend/src/lib/models/item.ts
-
7frontend/src/lib/models/project.ts
-
14frontend/src/lib/models/scope.ts
-
32frontend/src/lib/models/stat.ts
-
13frontend/src/lib/utils/slugify.ts
-
31frontend/src/routes/[scope].json.ts
-
29frontend/src/routes/[scope]/__layout.svelte
-
30frontend/src/routes/api/[...any]/index.ts
-
171frontend/src/routes/indexdata.json.ts
-
4internal/database/mysql/scopes.go
-
2internal/models/item.go
-
8internal/models/scope.go
@ -1,18 +1,3 @@ |
|||||
import type { Database } from "./database/interfaces"; |
|
||||
import MysqlDB from "./database/mysql/database"; |
|
||||
|
|
||||
let databasePromise: Promise<Database> | null = null; |
|
||||
let databaseTime: number = 0; |
|
||||
|
|
||||
const config = { |
|
||||
database() { |
|
||||
if (databasePromise == null || Date.now() < (databaseTime - 60000)) { |
|
||||
databasePromise = MysqlDB.connectEnv(); |
|
||||
databaseTime = Date.now(); |
|
||||
} |
|
||||
|
|
||||
return databasePromise; |
|
||||
} |
|
||||
}; |
|
||||
|
const config = {}; |
||||
|
|
||||
export default config; |
export default config; |
@ -1,37 +0,0 @@ |
|||||
import type { ScopeEntry, ScopeInput } from "$lib/models/scope"; |
|
||||
import type Scope from "$lib/models/scope"; |
|
||||
import type { StatEntry } from "$lib/models/stat"; |
|
||||
import type Stat from "$lib/models/stat"; |
|
||||
|
|
||||
export class NotFoundError extends Error { |
|
||||
constructor(subject: string) { |
|
||||
super(`${subject} not found`); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export interface Database { |
|
||||
userId: string |
|
||||
|
|
||||
scopes(): ScopeRepo |
|
||||
stats(scopeId: number): StatRepo |
|
||||
withUser(userId: string): Database |
|
||||
} |
|
||||
|
|
||||
export interface ScopeRepo { |
|
||||
userId: string |
|
||||
|
|
||||
find(id: number): Promise<Scope> |
|
||||
list(): Promise<ScopeEntry[]> |
|
||||
create(input: ScopeInput): Promise<Scope> |
|
||||
update(id: number, input: Partial<ScopeInput>): Promise<Scope> |
|
||||
delete(id: number): Promise<void> |
|
||||
} |
|
||||
|
|
||||
export interface StatRepo { |
|
||||
userId: string |
|
||||
scopeId: number |
|
||||
|
|
||||
find(id: number): Promise<Stat> |
|
||||
findEntries(...ids: number[]): Promise<StatEntry[]> |
|
||||
list(): Promise<Stat[]> |
|
||||
} |
|
@ -1,48 +0,0 @@ |
|||||
import {createPool} from "mysql2/promise" |
|
||||
import type {Pool} from "mysql2/promise"; |
|
||||
|
|
||||
import type { Database, ScopeRepo, StatRepo } from "../interfaces"; |
|
||||
import MysqlDBScopes from "./scopes"; |
|
||||
|
|
||||
|
|
||||
export default class MysqlDB implements Database { |
|
||||
connection: Pool |
|
||||
userId: string; |
|
||||
|
|
||||
private constructor(userId: string, connection: Pool) { |
|
||||
this.userId = userId; |
|
||||
this.connection = connection; |
|
||||
} |
|
||||
|
|
||||
scopes(): ScopeRepo { |
|
||||
return new MysqlDBScopes(this.connection, this.userId); |
|
||||
} |
|
||||
stats(scopeId: number): StatRepo { |
|
||||
throw new Error("Method not implemented."); |
|
||||
} |
|
||||
|
|
||||
withUser(userId: string): Database { |
|
||||
return new MysqlDB(userId, this.connection); |
|
||||
} |
|
||||
|
|
||||
static async connectEnv(): Promise<MysqlDB> { |
|
||||
return this.connect( |
|
||||
process.env.STUFFLOG3_MYSQL_HOST, |
|
||||
parseInt(process.env.STUFFLOG3_MYSQL_PORT), |
|
||||
process.env.STUFFLOG3_MYSQL_USERNAME, |
|
||||
process.env.STUFFLOG3_MYSQL_PASSWORD, |
|
||||
process.env.STUFFLOG3_MYSQL_SCHEMA, |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
static async connect(host: string, port: number, user: string, password: string, database: string): Promise<MysqlDB> { |
|
||||
const connection = await createPool({ |
|
||||
host, user, database, password, port, |
|
||||
waitForConnections: true, |
|
||||
connectionLimit: 20, |
|
||||
queueLimit: 0, |
|
||||
}); |
|
||||
|
|
||||
return new MysqlDB("", connection); |
|
||||
} |
|
||||
} |
|
@ -1,84 +0,0 @@ |
|||||
import type {Pool} from "mysql2/promise"; |
|
||||
|
|
||||
import type Scope from "$lib/models/scope"; |
|
||||
import type { ScopeEntry, ScopeInput } from "$lib/models/scope"; |
|
||||
import type { ScopeRepo } from "../interfaces"; |
|
||||
|
|
||||
export default class MysqlDBScopes implements ScopeRepo { |
|
||||
userId: string; |
|
||||
connection: Pool; |
|
||||
|
|
||||
constructor(connection: Pool, userId: string) { |
|
||||
this.connection = connection; |
|
||||
this.userId = userId; |
|
||||
} |
|
||||
|
|
||||
async find(id: number): Promise<Scope> { |
|
||||
const [[scopeRows], [projectRows], [statRows]] = await Promise.all([ |
|
||||
this.connection.execute(`
|
|
||||
SELECT scope.*, scope_member.name as display_name |
|
||||
FROM scope |
|
||||
INNER JOIN scope_member ON scope.id = scope_member.scope_id |
|
||||
WHERE scope.id = ? |
|
||||
`, [id]),
|
|
||||
this.connection.execute(`
|
|
||||
SELECT id,name,status |
|
||||
FROM project |
|
||||
WHERE scope_id = ? |
|
||||
`, [id]).catch(() => []),
|
|
||||
this.connection.execute(`
|
|
||||
SELECT * |
|
||||
FROM stats |
|
||||
WHERE scope_id = ? |
|
||||
`, [id]).catch(() => []),
|
|
||||
]); |
|
||||
|
|
||||
const r = scopeRows[0] as any; |
|
||||
if (!r) { |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
id: r.id, |
|
||||
name: r.name, |
|
||||
abbreviation: r.abbreviation, |
|
||||
displayName: r.display_name, |
|
||||
projects: ((projectRows||[]) as any[]).map((p:any) => ({ |
|
||||
id: p.id, |
|
||||
name: p.name, |
|
||||
status: p.status, |
|
||||
})), |
|
||||
stats: ((statRows||[]) as any[]).map((s:any) => ({ |
|
||||
id: s.id, |
|
||||
name: s.name, |
|
||||
weight: s.weight, |
|
||||
description: s.description, |
|
||||
allowedAmounts: s.allowed_amounts || void(0), |
|
||||
})), |
|
||||
} |
|
||||
} |
|
||||
async list(): Promise<ScopeEntry[]> { |
|
||||
const [rows] = await this.connection.execute(`
|
|
||||
SELECT scope.*, scope_member.name as display_name |
|
||||
FROM scope |
|
||||
INNER JOIN scope_member ON id = scope_id |
|
||||
WHERE user_id = ? |
|
||||
`, [this.userId]);
|
|
||||
|
|
||||
return (rows as any[]).map(r => ({ |
|
||||
id: r.id, |
|
||||
name: r.name, |
|
||||
abbreviation: r.abbreviation, |
|
||||
displayName: r.display_name, |
|
||||
})) |
|
||||
} |
|
||||
create(input: ScopeInput): Promise<Scope> { |
|
||||
throw new Error("Method not implemented."); |
|
||||
} |
|
||||
update(id: number, input: Partial<ScopeInput>): Promise<Scope> { |
|
||||
throw new Error("Method not implemented."); |
|
||||
} |
|
||||
delete(id: number): Promise<void> { |
|
||||
throw new Error("Method not implemented."); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,13 @@ |
|||||
|
export default function sluggify(s: string): string { |
||||
|
let out = ""; |
||||
|
|
||||
|
for (const c of s.toLocaleLowerCase()) { |
||||
|
if (c >= 'a' && c <= 'z') { |
||||
|
out += c; |
||||
|
} else if (!":'".includes(c)) { |
||||
|
out += "-"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return out; |
||||
|
} |
@ -1,31 +0,0 @@ |
|||||
import type { RequestHandler } from "@sveltejs/kit"; |
|
||||
|
|
||||
import config from "$lib/config"; |
|
||||
|
|
||||
export const get: RequestHandler = async({params, locals}) => { |
|
||||
const scopeId = parseInt(params.scope); |
|
||||
|
|
||||
const db = await config.database(); |
|
||||
const scope = await db.withUser(locals.user.id).scopes().find(scopeId) |
|
||||
if (scope === null) { |
|
||||
return { |
|
||||
status: 404, |
|
||||
headers: { |
|
||||
"Content-Type": "application/json" |
|
||||
}, |
|
||||
body: JSON.stringify({ |
|
||||
message: "Scope not found", |
|
||||
}), |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
status: 200, |
|
||||
headers: { |
|
||||
"Content-Type": "application/json" |
|
||||
}, |
|
||||
body: JSON.stringify({ |
|
||||
scope |
|
||||
}), |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,30 @@ |
|||||
|
import type { RequestHandler } from "@sveltejs/kit"; |
||||
|
|
||||
|
export const get: RequestHandler = async({ request, params }) => { |
||||
|
const proxyUrl = `${process.env.STUFFLOG3_API}/api/${params.any}`; |
||||
|
|
||||
|
console.log("PRX", proxyUrl); |
||||
|
const res = await fetch(proxyUrl, { |
||||
|
method: request.method, |
||||
|
headers: { |
||||
|
...request.headers, |
||||
|
}, |
||||
|
body: request.body, |
||||
|
}); |
||||
|
|
||||
|
const result = { |
||||
|
status: res.status, |
||||
|
body: await res.text(), |
||||
|
headers: {}, |
||||
|
} |
||||
|
res.headers.forEach((v, k) => { |
||||
|
result.headers[k] = v; |
||||
|
}) |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
export const options = get; |
||||
|
export const post = get; |
||||
|
export const put = get; |
||||
|
export const del = get; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue