mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Integerate the auth service to the api (#857)
This commit is contained in:
commit
6d09c3354c
@ -1,6 +1,8 @@
|
||||
# vi: ft=sh
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
KYOO_PREFIX=/api
|
||||
|
||||
# either an hard-coded secret to decode jwts or empty to use keibi's public secret.
|
||||
# this should only be used in tests
|
||||
JWT_SECRET=
|
||||
|
@ -24,6 +24,5 @@ WORKDIR /app
|
||||
COPY --from=builder /app/server server
|
||||
|
||||
ENV NODE_ENV=production
|
||||
EXPOSE 3000
|
||||
EXPOSE 3567
|
||||
CMD ["./server"]
|
||||
|
||||
|
10
api/Dockerfile.dev
Normal file
10
api/Dockerfile.dev
Normal file
@ -0,0 +1,10 @@
|
||||
FROM oven/bun AS builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json bun.lock .
|
||||
COPY patches patches
|
||||
RUN bun install --production
|
||||
|
||||
EXPOSE 3567
|
||||
CMD ["bun", "dev"]
|
||||
|
@ -4,12 +4,12 @@
|
||||
"": {
|
||||
"name": "api",
|
||||
"dependencies": {
|
||||
"@elysiajs/jwt": "^1.2.0",
|
||||
"@elysiajs/swagger": "^1.2.2",
|
||||
"blurhash": "^2.0.5",
|
||||
"drizzle-kit": "^0.30.4",
|
||||
"drizzle-orm": "0.39.0",
|
||||
"elysia": "^1.2.23",
|
||||
"jose": "^6.0.10",
|
||||
"parjs": "^1.3.9",
|
||||
"pg": "^8.13.3",
|
||||
"sharp": "^0.33.5",
|
||||
@ -27,8 +27,6 @@
|
||||
"packages": {
|
||||
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
||||
|
||||
"@elysiajs/jwt": ["@elysiajs/jwt@1.2.0", "", { "dependencies": { "jose": "^4.14.4" }, "peerDependencies": { "elysia": ">= 1.2.0" } }, "sha512-5iMoZucIKNAqPKW3n6RBIyCnDWG3kOcqA4WZKtqEff+IjV6AN3dlMSE2XsS0xjIvusLD0UBXS8cxQ9NwIcj6ew=="],
|
||||
|
||||
"@elysiajs/swagger": ["@elysiajs/swagger@1.2.2", "", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.2.0" } }, "sha512-DG0PbX/wzQNQ6kIpFFPCvmkkWTIbNWDS7lVLv3Puy6ONklF14B4NnbDfpYjX1hdSYKeCqKBBOuenh6jKm8tbYA=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.3.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw=="],
|
||||
@ -183,7 +181,7 @@
|
||||
|
||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
|
||||
"jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
|
||||
"jose": ["jose@6.0.10", "", {}, "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw=="],
|
||||
|
||||
"memoirist": ["memoirist@0.3.0", "", {}, "sha512-wR+4chMgVPq+T6OOsk40u9Wlpw1Pjx66NMNiYxCQQ4EUJ7jDs3D9kTCeKdBOkvAiqXlHLVJlvYL01PvIJ1MPNg=="],
|
||||
|
||||
|
@ -9,12 +9,12 @@
|
||||
"format": "biome check --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@elysiajs/jwt": "^1.2.0",
|
||||
"@elysiajs/swagger": "^1.2.2",
|
||||
"blurhash": "^2.0.5",
|
||||
"drizzle-kit": "^0.30.4",
|
||||
"drizzle-orm": "0.39.0",
|
||||
"elysia": "^1.2.23",
|
||||
"jose": "^6.0.10",
|
||||
"parjs": "^1.3.9",
|
||||
"pg": "^8.13.3",
|
||||
"sharp": "^0.33.5"
|
||||
|
47
api/src/auth.ts
Normal file
47
api/src/auth.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import Elysia, { getSchemaValidator, t } from "elysia";
|
||||
import { createRemoteJWKSet, jwtVerify } from "jose";
|
||||
import { KError } from "./models/error";
|
||||
|
||||
const jwtSecret = process.env.JWT_SECRET
|
||||
? new TextEncoder().encode(process.env.JWT_SECRET)
|
||||
: null;
|
||||
const jwks = createRemoteJWKSet(
|
||||
new URL(
|
||||
".well-known/jwks.json",
|
||||
process.env.AUTH_SERVER ?? "http://auth:4568",
|
||||
),
|
||||
);
|
||||
|
||||
const Jwt = t.Object({
|
||||
sub: t.String({ description: "User id" }),
|
||||
username: t.String(),
|
||||
sid: t.String({ description: "Session id" }),
|
||||
});
|
||||
const validator = getSchemaValidator(Jwt);
|
||||
|
||||
export const auth = new Elysia({ name: "auth" })
|
||||
.guard({
|
||||
// Those are not applied for now. See https://github.com/elysiajs/elysia/issues/1139
|
||||
detail: {
|
||||
security: [{ bearer: ["read"] }, { api: ["read"] }],
|
||||
},
|
||||
response: {
|
||||
401: { ...KError, description: "" },
|
||||
403: { ...KError, description: "" },
|
||||
},
|
||||
})
|
||||
.macro({
|
||||
permissions(perms: string[]) {
|
||||
return {
|
||||
resolve: async ({ headers: { authorization }, error }) => {
|
||||
const bearer = authorization?.slice(7);
|
||||
if (!bearer) return { jwt: false };
|
||||
// @ts-expect-error ts can't understand that there's two overload idk why
|
||||
const { payload } = await jwtVerify(bearer, jwtSecret ?? jwks);
|
||||
// TODO: use perms
|
||||
return { jwt: validator.Decode<typeof Jwt>(payload) };
|
||||
},
|
||||
};
|
||||
},
|
||||
})
|
||||
.as("plugin");
|
@ -1,4 +1,4 @@
|
||||
import { Elysia } from "elysia";
|
||||
import { Elysia, t } from "elysia";
|
||||
import { entriesH } from "./controllers/entries";
|
||||
import { imagesH } from "./controllers/images";
|
||||
import { seasonsH } from "./controllers/seasons";
|
||||
@ -44,9 +44,14 @@ export const base = new Elysia({ name: "base" })
|
||||
console.error(code, error);
|
||||
return error;
|
||||
})
|
||||
.get("/health", () => ({ status: "healthy" }) as const, {
|
||||
detail: { description: "Check if the api is healthy." },
|
||||
response: { 200: t.Object({ status: t.Literal("healthy") }) },
|
||||
})
|
||||
.as("plugin");
|
||||
|
||||
export const app = new Elysia()
|
||||
export const prefix = process.env.KYOO_PREFIX ?? "";
|
||||
export const app = new Elysia({ prefix })
|
||||
.use(base)
|
||||
.use(showsH)
|
||||
.use(movies)
|
@ -2,6 +2,7 @@ import { stat } from "node:fs/promises";
|
||||
import type { BunFile } from "bun";
|
||||
import { type SQL, and, eq, sql } from "drizzle-orm";
|
||||
import Elysia, { type Context, t } from "elysia";
|
||||
import { prefix } from "~/base";
|
||||
import { db } from "~/db";
|
||||
import {
|
||||
showTranslations,
|
||||
@ -87,8 +88,8 @@ function getRedirectToImageHandler({
|
||||
}
|
||||
set.headers["content-language"] = ret.language;
|
||||
return quality
|
||||
? redirect(`/images/${ret.image!.id}?quality=${quality}`)
|
||||
: redirect(`/images/${ret.image!.id}`);
|
||||
? redirect(`${prefix}/images/${ret.image!.id}?quality=${quality}`)
|
||||
: redirect(`${prefix}/images/${ret.image!.id}`);
|
||||
};
|
||||
}
|
||||
|
||||
@ -181,8 +182,8 @@ export const imagesH = new Elysia({ tags: ["images"] })
|
||||
});
|
||||
}
|
||||
return quality
|
||||
? redirect(`/images/${ret.image!.id}?quality=${quality}`)
|
||||
: redirect(`/images/${ret.image!.id}`);
|
||||
? redirect(`${prefix}/images/${ret.image!.id}?quality=${quality}`)
|
||||
: redirect(`${prefix}/images/${ret.image!.id}`);
|
||||
},
|
||||
{
|
||||
detail: { description: "Get the image of a staff member." },
|
||||
@ -256,8 +257,8 @@ export const imagesH = new Elysia({ tags: ["images"] })
|
||||
}
|
||||
set.headers["content-language"] = ret.language;
|
||||
return quality
|
||||
? redirect(`/images/${ret.image!.id}?quality=${quality}`)
|
||||
: redirect(`/images/${ret.image!.id}`);
|
||||
? redirect(`${prefix}/images/${ret.image!.id}?quality=${quality}`)
|
||||
: redirect(`${prefix}/images/${ret.image!.id}`);
|
||||
},
|
||||
{
|
||||
detail: { description: "Get the logo of a studio." },
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { and, eq, sql } from "drizzle-orm";
|
||||
import { Elysia, t } from "elysia";
|
||||
import { prefix } from "~/base";
|
||||
import { db } from "~/db";
|
||||
import { shows } from "~/db/schema";
|
||||
import {
|
||||
@ -113,7 +114,7 @@ export const collections = new Elysia({
|
||||
status: 404,
|
||||
message: "No collection in the database.",
|
||||
});
|
||||
return redirect(`/collections/${serie.slug}`);
|
||||
return redirect(`${prefix}/collections/${serie.slug}`);
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { and, eq, sql } from "drizzle-orm";
|
||||
import { Elysia, t } from "elysia";
|
||||
import { prefix } from "~/base";
|
||||
import { db } from "~/db";
|
||||
import { shows } from "~/db/schema";
|
||||
import { KError } from "~/models/error";
|
||||
@ -103,7 +104,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
|
||||
status: 404,
|
||||
message: "No movies in the database.",
|
||||
});
|
||||
return redirect(`/movies/${movie.slug}`);
|
||||
return redirect(`${prefix}/movies/${movie.slug}`);
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { and, eq, sql } from "drizzle-orm";
|
||||
import { Elysia, t } from "elysia";
|
||||
import { prefix } from "~/base";
|
||||
import { db } from "~/db";
|
||||
import { shows } from "~/db/schema";
|
||||
import { KError } from "~/models/error";
|
||||
@ -45,7 +46,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] })
|
||||
if (!ret) {
|
||||
return error(404, {
|
||||
status: 404,
|
||||
message: "Movie not found",
|
||||
message: `No serie found with the id or slug: '${id}'.`,
|
||||
});
|
||||
}
|
||||
if (!ret.language) {
|
||||
@ -103,7 +104,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] })
|
||||
status: 404,
|
||||
message: "No series in the database.",
|
||||
});
|
||||
return redirect(`/series/${serie.slug}`);
|
||||
return redirect(`${prefix}/series/${serie.slug}`);
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { and, isNull, sql } from "drizzle-orm";
|
||||
import { Elysia, t } from "elysia";
|
||||
import { prefix } from "~/base";
|
||||
import { db } from "~/db";
|
||||
import { shows } from "~/db/schema";
|
||||
import { KError } from "~/models/error";
|
||||
@ -31,7 +32,7 @@ export const showsH = new Elysia({ prefix: "/shows", tags: ["shows"] })
|
||||
status: 404,
|
||||
message: "No shows in the database.",
|
||||
});
|
||||
return redirect(`/${show.kind}s/${show.slug}`);
|
||||
return redirect(`${prefix}/${show.kind}s/${show.slug}`);
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { type SQL, and, eq, sql } from "drizzle-orm";
|
||||
import Elysia, { t } from "elysia";
|
||||
import { prefix } from "~/base";
|
||||
import { db } from "~/db";
|
||||
import { showTranslations, shows } from "~/db/schema";
|
||||
import { roles, staff } from "~/db/schema/staff";
|
||||
@ -160,7 +161,7 @@ export const staffH = new Elysia({ tags: ["staff"] })
|
||||
status: 404,
|
||||
message: "No staff in the database.",
|
||||
});
|
||||
return redirect(`/staff/${member.slug}`);
|
||||
return redirect(`${prefix}/staff/${member.slug}`);
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { type SQL, and, eq, exists, sql } from "drizzle-orm";
|
||||
import Elysia, { t } from "elysia";
|
||||
import { prefix } from "~/base";
|
||||
import { db } from "~/db";
|
||||
import {
|
||||
showStudioJoin,
|
||||
@ -200,7 +201,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] })
|
||||
status: 404,
|
||||
message: "No studios in the database.",
|
||||
});
|
||||
return redirect(`/studios/${studio.slug}`);
|
||||
return redirect(`${prefix}/studios/${studio.slug}`);
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
|
@ -1,34 +1,15 @@
|
||||
import jwt from "@elysiajs/jwt";
|
||||
import { swagger } from "@elysiajs/swagger";
|
||||
import { app } from "./base";
|
||||
import { processImages } from "./controllers/seed/images";
|
||||
import { migrate } from "./db";
|
||||
import { app } from "./elysia";
|
||||
import { comment } from "./utils";
|
||||
|
||||
await migrate();
|
||||
|
||||
let secret = process.env.JWT_SECRET;
|
||||
if (!secret) {
|
||||
const auth = process.env.AUTH_SERVER ?? "http://auth:4568";
|
||||
try {
|
||||
const ret = await fetch(`${auth}/info`);
|
||||
const info = await ret.json();
|
||||
secret = info.publicKey;
|
||||
} catch (error) {
|
||||
console.error(`Can't access auth server at ${auth}:\n${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!secret) {
|
||||
console.error("Missing jwt secret or auth server. exiting");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// run image processor task in background
|
||||
processImages();
|
||||
|
||||
app
|
||||
.use(jwt({ secret }))
|
||||
.use(
|
||||
swagger({
|
||||
documentation: {
|
||||
@ -74,9 +55,23 @@ app
|
||||
description: "Routes about images: posters, thumbnails...",
|
||||
},
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
bearer: {
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
bearerFormat: "opaque",
|
||||
},
|
||||
api: {
|
||||
type: "apiKey",
|
||||
in: "header",
|
||||
name: "X-API-KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
.listen(3000);
|
||||
.listen(3567);
|
||||
|
||||
console.log(`Api running at ${app.server?.hostname}:${app.server?.port}`);
|
||||
|
@ -4,4 +4,4 @@ export * from "./studio-helper";
|
||||
export * from "./staff-helper";
|
||||
export * from "./videos-helper";
|
||||
|
||||
export * from "~/elysia";
|
||||
export * from "~/base";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { buildUrl } from "tests/utils";
|
||||
import { app } from "~/elysia";
|
||||
import { app } from "~/base";
|
||||
import type { SeedMovie } from "~/models/movie";
|
||||
|
||||
export const getMovie = async (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { buildUrl } from "tests/utils";
|
||||
import { app } from "~/elysia";
|
||||
import { app } from "~/base";
|
||||
import type { SeedSerie } from "~/models/serie";
|
||||
|
||||
export const createSerie = async (serie: SeedSerie) => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { buildUrl } from "tests/utils";
|
||||
import { app } from "~/elysia";
|
||||
import { app } from "~/base";
|
||||
|
||||
export const getStaff = async (id: string, query: {}) => {
|
||||
const resp = await app.handle(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { buildUrl } from "tests/utils";
|
||||
import { app } from "~/elysia";
|
||||
import { app } from "~/base";
|
||||
|
||||
export const getStudio = async (
|
||||
id: string,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { buildUrl } from "tests/utils";
|
||||
import { app } from "~/elysia";
|
||||
import { app } from "~/base";
|
||||
import type { SeedVideo } from "~/models/video";
|
||||
|
||||
export const createVideo = async (video: SeedVideo | SeedVideo[]) => {
|
||||
|
@ -4,6 +4,9 @@
|
||||
# http route prefix (will listen to $KEIBI_PREFIX/users for example)
|
||||
KEIBI_PREFIX=""
|
||||
|
||||
# The url you can use to reach your kyoo instance. This is used during oidc to redirect users to your instance.
|
||||
PUBLIC_URL=http://localhost:8901
|
||||
|
||||
# Database things
|
||||
POSTGRES_USER=kyoo
|
||||
POSTGRES_PASSWORD=password
|
||||
|
4
auth/.gitignore
vendored
4
auth/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
# generated via sqlc
|
||||
dbc/
|
||||
# genereated via swag
|
||||
docs/
|
@ -23,4 +23,5 @@ USER nonroot:nonroot
|
||||
COPY --from=build /keibi /app/keibi
|
||||
COPY sql ./sql
|
||||
|
||||
HEALTHCHECK --interval=30s --retries=15 CMD curl --fail http://localhost:4568$KEIBI_PREFIX/health || exit
|
||||
CMD ["/app/keibi"]
|
||||
|
@ -2,15 +2,10 @@ FROM golang:1.24 AS build
|
||||
WORKDIR /app
|
||||
|
||||
RUN go install github.com/bokwoon95/wgo@latest
|
||||
RUN go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
|
||||
RUN go install github.com/swaggo/swag/cmd/swag@latest
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# COPY sqlc.yaml ./
|
||||
# COPY sql/ ./
|
||||
# RUN sqlc generate
|
||||
|
||||
EXPOSE 4568
|
||||
HEALTHCHECK --interval=30s --retries=15 CMD curl --fail http://localhost:4568$KEIBI_PREFIX/health || exit
|
||||
CMD ["wgo", "run", "-race", "."]
|
||||
|
@ -17,13 +17,12 @@ type Configuration struct {
|
||||
Prefix string
|
||||
JwtPrivateKey *rsa.PrivateKey
|
||||
JwtPublicKey *rsa.PublicKey
|
||||
Issuer string
|
||||
PublicUrl string
|
||||
DefaultClaims jwt.MapClaims
|
||||
ExpirationDelay time.Duration
|
||||
}
|
||||
|
||||
var DefaultConfig = Configuration{
|
||||
Issuer: "kyoo",
|
||||
DefaultClaims: make(jwt.MapClaims),
|
||||
ExpirationDelay: 30 * 24 * time.Hour,
|
||||
}
|
||||
@ -54,6 +53,7 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
|
||||
}
|
||||
}
|
||||
|
||||
ret.PublicUrl = os.Getenv("PUBLIC_URL")
|
||||
ret.Prefix = os.Getenv("KEIBI_PREFIX")
|
||||
|
||||
if ret.JwtPrivateKey == nil {
|
||||
|
73
auth/dbc/config.sql.go
Normal file
73
auth/dbc/config.sql.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// source: config.sql
|
||||
|
||||
package dbc
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const deleteConfig = `-- name: DeleteConfig :one
|
||||
delete from config
|
||||
where key = $1
|
||||
returning
|
||||
key, value
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteConfig(ctx context.Context, key string) (Config, error) {
|
||||
row := q.db.QueryRow(ctx, deleteConfig, key)
|
||||
var i Config
|
||||
err := row.Scan(&i.Key, &i.Value)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const loadConfig = `-- name: LoadConfig :many
|
||||
select
|
||||
key, value
|
||||
from
|
||||
config
|
||||
`
|
||||
|
||||
func (q *Queries) LoadConfig(ctx context.Context) ([]Config, error) {
|
||||
rows, err := q.db.Query(ctx, loadConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Config
|
||||
for rows.Next() {
|
||||
var i Config
|
||||
if err := rows.Scan(&i.Key, &i.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const saveConfig = `-- name: SaveConfig :one
|
||||
insert into config(key, value)
|
||||
values ($1, $2)
|
||||
on conflict (key)
|
||||
do update set
|
||||
value = excluded.value
|
||||
returning
|
||||
key, value
|
||||
`
|
||||
|
||||
type SaveConfigParams struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func (q *Queries) SaveConfig(ctx context.Context, arg SaveConfigParams) (Config, error) {
|
||||
row := q.db.QueryRow(ctx, saveConfig, arg.Key, arg.Value)
|
||||
var i Config
|
||||
err := row.Scan(&i.Key, &i.Value)
|
||||
return i, err
|
||||
}
|
32
auth/dbc/db.go
Normal file
32
auth/dbc/db.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
|
||||
package dbc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
49
auth/dbc/models.go
Normal file
49
auth/dbc/models.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
|
||||
package dbc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
jwt "github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type OidcHandle struct {
|
||||
UserPk int32 `json:"userPk"`
|
||||
Provider string `json:"provider"`
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
ProfileUrl *string `json:"profileUrl"`
|
||||
AccessToken *string `json:"accessToken"`
|
||||
RefreshToken *string `json:"refreshToken"`
|
||||
ExpireAt *time.Time `json:"expireAt"`
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
Pk int32 `json:"pk"`
|
||||
Id uuid.UUID `json:"id"`
|
||||
Token string `json:"token"`
|
||||
UserPk int32 `json:"userPk"`
|
||||
CreatedDate time.Time `json:"createdDate"`
|
||||
LastUsed time.Time `json:"lastUsed"`
|
||||
Device *string `json:"device"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Pk int32 `json:"pk"`
|
||||
Id uuid.UUID `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password *string `json:"password"`
|
||||
Claims jwt.MapClaims `json:"claims"`
|
||||
CreatedDate time.Time `json:"createdDate"`
|
||||
LastSeen time.Time `json:"lastSeen"`
|
||||
}
|
161
auth/dbc/sessions.sql.go
Normal file
161
auth/dbc/sessions.sql.go
Normal file
@ -0,0 +1,161 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// source: sessions.sql
|
||||
|
||||
package dbc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createSession = `-- name: CreateSession :one
|
||||
insert into sessions(token, user_pk, device)
|
||||
values ($1, $2, $3)
|
||||
returning
|
||||
pk, id, token, user_pk, created_date, last_used, device
|
||||
`
|
||||
|
||||
type CreateSessionParams struct {
|
||||
Token string `json:"token"`
|
||||
UserPk int32 `json:"userPk"`
|
||||
Device *string `json:"device"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
|
||||
row := q.db.QueryRow(ctx, createSession, arg.Token, arg.UserPk, arg.Device)
|
||||
var i Session
|
||||
err := row.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Token,
|
||||
&i.UserPk,
|
||||
&i.CreatedDate,
|
||||
&i.LastUsed,
|
||||
&i.Device,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteSession = `-- name: DeleteSession :one
|
||||
delete from sessions as s using users as u
|
||||
where s.user_pk = u.pk
|
||||
and s.id = $1
|
||||
and u.id = $2
|
||||
returning
|
||||
s.pk, s.id, s.token, s.user_pk, s.created_date, s.last_used, s.device
|
||||
`
|
||||
|
||||
type DeleteSessionParams struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
UserId uuid.UUID `json:"userId"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteSession(ctx context.Context, arg DeleteSessionParams) (Session, error) {
|
||||
row := q.db.QueryRow(ctx, deleteSession, arg.Id, arg.UserId)
|
||||
var i Session
|
||||
err := row.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Token,
|
||||
&i.UserPk,
|
||||
&i.CreatedDate,
|
||||
&i.LastUsed,
|
||||
&i.Device,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserFromToken = `-- name: GetUserFromToken :one
|
||||
select
|
||||
s.id,
|
||||
s.last_used,
|
||||
u.pk, u.id, u.username, u.email, u.password, u.claims, u.created_date, u.last_seen
|
||||
from
|
||||
users as u
|
||||
inner join sessions as s on u.pk = s.user_pk
|
||||
where
|
||||
s.token = $1
|
||||
limit 1
|
||||
`
|
||||
|
||||
type GetUserFromTokenRow struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
LastUsed time.Time `json:"lastUsed"`
|
||||
User User `json:"user"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserFromToken(ctx context.Context, token string) (GetUserFromTokenRow, error) {
|
||||
row := q.db.QueryRow(ctx, getUserFromToken, token)
|
||||
var i GetUserFromTokenRow
|
||||
err := row.Scan(
|
||||
&i.Id,
|
||||
&i.LastUsed,
|
||||
&i.User.Pk,
|
||||
&i.User.Id,
|
||||
&i.User.Username,
|
||||
&i.User.Email,
|
||||
&i.User.Password,
|
||||
&i.User.Claims,
|
||||
&i.User.CreatedDate,
|
||||
&i.User.LastSeen,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserSessions = `-- name: GetUserSessions :many
|
||||
select
|
||||
s.pk, s.id, s.token, s.user_pk, s.created_date, s.last_used, s.device
|
||||
from
|
||||
sessions as s
|
||||
inner join users as u on u.pk = s.user_pk
|
||||
where
|
||||
u.pk = $1
|
||||
order by
|
||||
last_used
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserSessions(ctx context.Context, pk int32) ([]Session, error) {
|
||||
rows, err := q.db.Query(ctx, getUserSessions, pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Session
|
||||
for rows.Next() {
|
||||
var i Session
|
||||
if err := rows.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Token,
|
||||
&i.UserPk,
|
||||
&i.CreatedDate,
|
||||
&i.LastUsed,
|
||||
&i.Device,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const touchSession = `-- name: TouchSession :exec
|
||||
update
|
||||
sessions
|
||||
set
|
||||
last_used = now()::timestamptz
|
||||
where
|
||||
id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) TouchSession(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := q.db.Exec(ctx, touchSession, id)
|
||||
return err
|
||||
}
|
296
auth/dbc/users.sql.go
Normal file
296
auth/dbc/users.sql.go
Normal file
@ -0,0 +1,296 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// source: users.sql
|
||||
|
||||
package dbc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
jwt "github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createUser = `-- name: CreateUser :one
|
||||
insert into users(username, email, password, claims)
|
||||
values ($1, $2, $3, $4)
|
||||
returning
|
||||
pk, id, username, email, password, claims, created_date, last_seen
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password *string `json:"password"`
|
||||
Claims jwt.MapClaims `json:"claims"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, createUser,
|
||||
arg.Username,
|
||||
arg.Email,
|
||||
arg.Password,
|
||||
arg.Claims,
|
||||
)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.Claims,
|
||||
&i.CreatedDate,
|
||||
&i.LastSeen,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteUser = `-- name: DeleteUser :one
|
||||
delete from users
|
||||
where id = $1
|
||||
returning
|
||||
pk, id, username, email, password, claims, created_date, last_seen
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteUser(ctx context.Context, id uuid.UUID) (User, error) {
|
||||
row := q.db.QueryRow(ctx, deleteUser, id)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.Claims,
|
||||
&i.CreatedDate,
|
||||
&i.LastSeen,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getAllUsers = `-- name: GetAllUsers :many
|
||||
select
|
||||
pk, id, username, email, password, claims, created_date, last_seen
|
||||
from
|
||||
users
|
||||
order by
|
||||
id
|
||||
limit $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllUsers(ctx context.Context, limit int32) ([]User, error) {
|
||||
rows, err := q.db.Query(ctx, getAllUsers, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []User
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.Claims,
|
||||
&i.CreatedDate,
|
||||
&i.LastSeen,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getAllUsersAfter = `-- name: GetAllUsersAfter :many
|
||||
select
|
||||
pk, id, username, email, password, claims, created_date, last_seen
|
||||
from
|
||||
users
|
||||
where
|
||||
id >= $2
|
||||
order by
|
||||
id
|
||||
limit $1
|
||||
`
|
||||
|
||||
type GetAllUsersAfterParams struct {
|
||||
Limit int32 `json:"limit"`
|
||||
AfterId uuid.UUID `json:"afterId"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllUsersAfter(ctx context.Context, arg GetAllUsersAfterParams) ([]User, error) {
|
||||
rows, err := q.db.Query(ctx, getAllUsersAfter, arg.Limit, arg.AfterId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []User
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.Claims,
|
||||
&i.CreatedDate,
|
||||
&i.LastSeen,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUser = `-- name: GetUser :many
|
||||
select
|
||||
u.pk, u.id, u.username, u.email, u.password, u.claims, u.created_date, u.last_seen,
|
||||
h.provider,
|
||||
h.id,
|
||||
h.username,
|
||||
h.profile_url
|
||||
from
|
||||
users as u
|
||||
left join oidc_handle as h on u.pk = h.user_pk
|
||||
where
|
||||
u.id = $1
|
||||
`
|
||||
|
||||
type GetUserRow struct {
|
||||
User User `json:"user"`
|
||||
Provider *string `json:"provider"`
|
||||
Id *string `json:"id"`
|
||||
Username *string `json:"username"`
|
||||
ProfileUrl *string `json:"profileUrl"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, id uuid.UUID) ([]GetUserRow, error) {
|
||||
rows, err := q.db.Query(ctx, getUser, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetUserRow
|
||||
for rows.Next() {
|
||||
var i GetUserRow
|
||||
if err := rows.Scan(
|
||||
&i.User.Pk,
|
||||
&i.User.Id,
|
||||
&i.User.Username,
|
||||
&i.User.Email,
|
||||
&i.User.Password,
|
||||
&i.User.Claims,
|
||||
&i.User.CreatedDate,
|
||||
&i.User.LastSeen,
|
||||
&i.Provider,
|
||||
&i.Id,
|
||||
&i.Username,
|
||||
&i.ProfileUrl,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUserByLogin = `-- name: GetUserByLogin :one
|
||||
select
|
||||
pk, id, username, email, password, claims, created_date, last_seen
|
||||
from
|
||||
users
|
||||
where
|
||||
email = $1
|
||||
or username = $1
|
||||
limit 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByLogin(ctx context.Context, login string) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByLogin, login)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.Claims,
|
||||
&i.CreatedDate,
|
||||
&i.LastSeen,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const touchUser = `-- name: TouchUser :exec
|
||||
update
|
||||
users
|
||||
set
|
||||
last_used = now()::timestamptz
|
||||
where
|
||||
id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) TouchUser(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := q.db.Exec(ctx, touchUser, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUser = `-- name: UpdateUser :one
|
||||
update
|
||||
users
|
||||
set
|
||||
username = $2,
|
||||
email = $3,
|
||||
password = $4,
|
||||
claims = $5
|
||||
where
|
||||
id = $1
|
||||
returning
|
||||
pk, id, username, email, password, claims, created_date, last_seen
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password *string `json:"password"`
|
||||
Claims jwt.MapClaims `json:"claims"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, updateUser,
|
||||
arg.Id,
|
||||
arg.Username,
|
||||
arg.Email,
|
||||
arg.Password,
|
||||
arg.Claims,
|
||||
)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.Pk,
|
||||
&i.Id,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.Claims,
|
||||
&i.CreatedDate,
|
||||
&i.LastSeen,
|
||||
)
|
||||
return i, err
|
||||
}
|
650
auth/docs/docs.go
Normal file
650
auth/docs/docs.go
Normal file
@ -0,0 +1,650 @@
|
||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {
|
||||
"name": "Repository",
|
||||
"url": "https://github.com/zoriya/kyoo"
|
||||
},
|
||||
"license": {
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/info": {
|
||||
"get": {
|
||||
"description": "Get info like the public key used to sign the jwts.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"jwt"
|
||||
],
|
||||
"summary": "Info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/jwt": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Token": []
|
||||
}
|
||||
],
|
||||
"description": "Convert a session token to a short lived JWT.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"jwt"
|
||||
],
|
||||
"summary": "Get JWT",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Jwt"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing session token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid session token (or expired)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions": {
|
||||
"post": {
|
||||
"description": "Login to your account and open a session",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "Login",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The device the created session will be used on",
|
||||
"name": "device",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"description": "Account informations",
|
||||
"name": "login",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.LoginDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dbc.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid login body",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid password",
|
||||
"schema": {}
|
||||
},
|
||||
"404": {
|
||||
"description": "Account does not exists",
|
||||
"schema": {}
|
||||
},
|
||||
"422": {
|
||||
"description": "User does not have a password (registered via oidc, please login via oidc)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions/current": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete a session and logout",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "Logout",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid session id",
|
||||
"schema": {}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {}
|
||||
},
|
||||
"404": {
|
||||
"description": "Session not found with specified id (if not using the /current route)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions/{id}": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete a session and logout",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "Logout",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "The id of the session to delete",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid session id",
|
||||
"schema": {}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {}
|
||||
},
|
||||
"404": {
|
||||
"description": "Session not found with specified id (if not using the /current route)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.read"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "List all users existing in this instance.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "List all users",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "used for pagination.",
|
||||
"name": "afterId",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid after id",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Register as a new user and open a session for it",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Register",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The device the created session will be used on",
|
||||
"name": "device",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"description": "Registration informations",
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.RegisterDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dbc.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid register body",
|
||||
"schema": {}
|
||||
},
|
||||
"409": {
|
||||
"description": "Duplicated email or username",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Get informations about the currently connected user",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get me",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete your account and all your sessions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete self",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.read"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Get informations about a user from it's id",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "The id of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No user with the given id found",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.delete"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Delete an account and all it's sessions.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "User id of the user to delete",
|
||||
"name": "id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Invalid user id",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"dbc.Session": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"device": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastUsed": {
|
||||
"type": "string"
|
||||
},
|
||||
"pk": {
|
||||
"type": "integer"
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
},
|
||||
"userPk": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"publicKey": {
|
||||
"description": "The public key used to sign jwt tokens. It can be used by your services to check if the jwt is valid.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Jwt": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"description": "The jwt token you can use for all authorized call to either keibi or other services.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.LoginDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"login",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"login": {
|
||||
"description": "Either the email or the username.",
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password of the account.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.OidcHandle": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this oidc handle.",
|
||||
"type": "string"
|
||||
},
|
||||
"profileUrl": {
|
||||
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user on the external service.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.RegisterDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Valid email that could be used for forgotten password requests. Can be used for login.",
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password to use.",
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the new account, can't contain @ signs. Can be used for login.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Session": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdDate": {
|
||||
"description": "When was the session first opened",
|
||||
"type": "string"
|
||||
},
|
||||
"device": {
|
||||
"description": "Device that created the session.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "Unique id of this session. Can be used for calls to DELETE",
|
||||
"type": "string"
|
||||
},
|
||||
"lastUsed": {
|
||||
"description": "Last date this session was used to access a service.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
"description": "List of custom claims JWT created via get /jwt will have",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"createdDate": {
|
||||
"description": "When was this account created?",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "Email of the user. Can be used as a login.",
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"id": {
|
||||
"description": "Id of the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"lastSeen": {
|
||||
"description": "When was the last time this account made any authorized request?",
|
||||
"type": "string"
|
||||
},
|
||||
"oidc": {
|
||||
"description": "List of other login method available for this user. Access tokens wont be returned here.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/main.OidcHandle"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user. Can be used as a login.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"Jwt": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
},
|
||||
"Token": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "kyoo.zoriya.dev",
|
||||
BasePath: "/auth",
|
||||
Schemes: []string{},
|
||||
Title: "Keibi - Kyoo's auth",
|
||||
Description: "Auth system made for kyoo.",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
626
auth/docs/swagger.json
Normal file
626
auth/docs/swagger.json
Normal file
@ -0,0 +1,626 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "Auth system made for kyoo.",
|
||||
"title": "Keibi - Kyoo's auth",
|
||||
"contact": {
|
||||
"name": "Repository",
|
||||
"url": "https://github.com/zoriya/kyoo"
|
||||
},
|
||||
"license": {
|
||||
"name": "GPL-3.0",
|
||||
"url": "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "kyoo.zoriya.dev",
|
||||
"basePath": "/auth",
|
||||
"paths": {
|
||||
"/info": {
|
||||
"get": {
|
||||
"description": "Get info like the public key used to sign the jwts.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"jwt"
|
||||
],
|
||||
"summary": "Info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/jwt": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Token": []
|
||||
}
|
||||
],
|
||||
"description": "Convert a session token to a short lived JWT.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"jwt"
|
||||
],
|
||||
"summary": "Get JWT",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Jwt"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing session token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid session token (or expired)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions": {
|
||||
"post": {
|
||||
"description": "Login to your account and open a session",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "Login",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The device the created session will be used on",
|
||||
"name": "device",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"description": "Account informations",
|
||||
"name": "login",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.LoginDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dbc.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid login body",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid password",
|
||||
"schema": {}
|
||||
},
|
||||
"404": {
|
||||
"description": "Account does not exists",
|
||||
"schema": {}
|
||||
},
|
||||
"422": {
|
||||
"description": "User does not have a password (registered via oidc, please login via oidc)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions/current": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete a session and logout",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "Logout",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid session id",
|
||||
"schema": {}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {}
|
||||
},
|
||||
"404": {
|
||||
"description": "Session not found with specified id (if not using the /current route)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/sessions/{id}": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete a session and logout",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"sessions"
|
||||
],
|
||||
"summary": "Logout",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "The id of the session to delete",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid session id",
|
||||
"schema": {}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {}
|
||||
},
|
||||
"404": {
|
||||
"description": "Session not found with specified id (if not using the /current route)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.read"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "List all users existing in this instance.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "List all users",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "used for pagination.",
|
||||
"name": "afterId",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid after id",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Register as a new user and open a session for it",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Register",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The device the created session will be used on",
|
||||
"name": "device",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"description": "Registration informations",
|
||||
"name": "user",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.RegisterDto"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dbc.Session"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid register body",
|
||||
"schema": {}
|
||||
},
|
||||
"409": {
|
||||
"description": "Duplicated email or username",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/me": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Get informations about the currently connected user",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get me",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Missing jwt token",
|
||||
"schema": {}
|
||||
},
|
||||
"403": {
|
||||
"description": "Invalid jwt token (or expired)",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": []
|
||||
}
|
||||
],
|
||||
"description": "Delete your account and all your sessions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete self",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/{id}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.read"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Get informations about a user from it's id",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "The id of the user",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No user with the given id found",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Jwt": [
|
||||
"users.delete"
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Delete an account and all it's sessions.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "User id of the user to delete",
|
||||
"name": "id",
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/main.User"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Invalid user id",
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"dbc.Session": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"device": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastUsed": {
|
||||
"type": "string"
|
||||
},
|
||||
"pk": {
|
||||
"type": "integer"
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
},
|
||||
"userPk": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"publicKey": {
|
||||
"description": "The public key used to sign jwt tokens. It can be used by your services to check if the jwt is valid.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Jwt": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"description": "The jwt token you can use for all authorized call to either keibi or other services.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.LoginDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"login",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"login": {
|
||||
"description": "Either the email or the username.",
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password of the account.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.OidcHandle": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Id of this oidc handle.",
|
||||
"type": "string"
|
||||
},
|
||||
"profileUrl": {
|
||||
"description": "Link to the profile of the user on the external service. Null if unknown or irrelevant.",
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user on the external service.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.RegisterDto": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Valid email that could be used for forgotten password requests. Can be used for login.",
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password to use.",
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the new account, can't contain @ signs. Can be used for login.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.Session": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"createdDate": {
|
||||
"description": "When was the session first opened",
|
||||
"type": "string"
|
||||
},
|
||||
"device": {
|
||||
"description": "Device that created the session.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "Unique id of this session. Can be used for calls to DELETE",
|
||||
"type": "string"
|
||||
},
|
||||
"lastUsed": {
|
||||
"description": "Last date this session was used to access a service.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main.User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claims": {
|
||||
"description": "List of custom claims JWT created via get /jwt will have",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"createdDate": {
|
||||
"description": "When was this account created?",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "Email of the user. Can be used as a login.",
|
||||
"type": "string",
|
||||
"format": "email"
|
||||
},
|
||||
"id": {
|
||||
"description": "Id of the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"lastSeen": {
|
||||
"description": "When was the last time this account made any authorized request?",
|
||||
"type": "string"
|
||||
},
|
||||
"oidc": {
|
||||
"description": "List of other login method available for this user. Access tokens wont be returned here.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/main.OidcHandle"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
"description": "Username of the user. Can be used as a login.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"Jwt": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
},
|
||||
"Token": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
426
auth/docs/swagger.yaml
Normal file
426
auth/docs/swagger.yaml
Normal file
@ -0,0 +1,426 @@
|
||||
basePath: /auth
|
||||
definitions:
|
||||
dbc.Session:
|
||||
properties:
|
||||
createdDate:
|
||||
type: string
|
||||
device:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
lastUsed:
|
||||
type: string
|
||||
pk:
|
||||
type: integer
|
||||
token:
|
||||
type: string
|
||||
userPk:
|
||||
type: integer
|
||||
type: object
|
||||
main.Info:
|
||||
properties:
|
||||
publicKey:
|
||||
description: The public key used to sign jwt tokens. It can be used by your
|
||||
services to check if the jwt is valid.
|
||||
type: string
|
||||
type: object
|
||||
main.Jwt:
|
||||
properties:
|
||||
token:
|
||||
description: The jwt token you can use for all authorized call to either keibi
|
||||
or other services.
|
||||
type: string
|
||||
type: object
|
||||
main.LoginDto:
|
||||
properties:
|
||||
login:
|
||||
description: Either the email or the username.
|
||||
type: string
|
||||
password:
|
||||
description: Password of the account.
|
||||
type: string
|
||||
required:
|
||||
- login
|
||||
- password
|
||||
type: object
|
||||
main.OidcHandle:
|
||||
properties:
|
||||
id:
|
||||
description: Id of this oidc handle.
|
||||
type: string
|
||||
profileUrl:
|
||||
description: Link to the profile of the user on the external service. Null
|
||||
if unknown or irrelevant.
|
||||
format: url
|
||||
type: string
|
||||
username:
|
||||
description: Username of the user on the external service.
|
||||
type: string
|
||||
type: object
|
||||
main.RegisterDto:
|
||||
properties:
|
||||
email:
|
||||
description: Valid email that could be used for forgotten password requests.
|
||||
Can be used for login.
|
||||
format: email
|
||||
type: string
|
||||
password:
|
||||
description: Password to use.
|
||||
type: string
|
||||
username:
|
||||
description: Username of the new account, can't contain @ signs. Can be used
|
||||
for login.
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
- password
|
||||
- username
|
||||
type: object
|
||||
main.Session:
|
||||
properties:
|
||||
createdDate:
|
||||
description: When was the session first opened
|
||||
type: string
|
||||
device:
|
||||
description: Device that created the session.
|
||||
type: string
|
||||
id:
|
||||
description: Unique id of this session. Can be used for calls to DELETE
|
||||
type: string
|
||||
lastUsed:
|
||||
description: Last date this session was used to access a service.
|
||||
type: string
|
||||
type: object
|
||||
main.User:
|
||||
properties:
|
||||
claims:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: List of custom claims JWT created via get /jwt will have
|
||||
type: object
|
||||
createdDate:
|
||||
description: When was this account created?
|
||||
type: string
|
||||
email:
|
||||
description: Email of the user. Can be used as a login.
|
||||
format: email
|
||||
type: string
|
||||
id:
|
||||
description: Id of the user.
|
||||
type: string
|
||||
lastSeen:
|
||||
description: When was the last time this account made any authorized request?
|
||||
type: string
|
||||
oidc:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/main.OidcHandle'
|
||||
description: List of other login method available for this user. Access tokens
|
||||
wont be returned here.
|
||||
type: object
|
||||
username:
|
||||
description: Username of the user. Can be used as a login.
|
||||
type: string
|
||||
type: object
|
||||
host: kyoo.zoriya.dev
|
||||
info:
|
||||
contact:
|
||||
name: Repository
|
||||
url: https://github.com/zoriya/kyoo
|
||||
description: Auth system made for kyoo.
|
||||
license:
|
||||
name: GPL-3.0
|
||||
url: https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
title: Keibi - Kyoo's auth
|
||||
version: "1.0"
|
||||
paths:
|
||||
/info:
|
||||
get:
|
||||
description: Get info like the public key used to sign the jwts.
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.Info'
|
||||
summary: Info
|
||||
tags:
|
||||
- jwt
|
||||
/jwt:
|
||||
get:
|
||||
description: Convert a session token to a short lived JWT.
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.Jwt'
|
||||
"401":
|
||||
description: Missing session token
|
||||
schema: {}
|
||||
"403":
|
||||
description: Invalid session token (or expired)
|
||||
schema: {}
|
||||
security:
|
||||
- Token: []
|
||||
summary: Get JWT
|
||||
tags:
|
||||
- jwt
|
||||
/sessions:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Login to your account and open a session
|
||||
parameters:
|
||||
- description: The device the created session will be used on
|
||||
in: query
|
||||
name: device
|
||||
type: string
|
||||
- description: Account informations
|
||||
in: body
|
||||
name: login
|
||||
schema:
|
||||
$ref: '#/definitions/main.LoginDto'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/dbc.Session'
|
||||
"400":
|
||||
description: Invalid login body
|
||||
schema: {}
|
||||
"403":
|
||||
description: Invalid password
|
||||
schema: {}
|
||||
"404":
|
||||
description: Account does not exists
|
||||
schema: {}
|
||||
"422":
|
||||
description: User does not have a password (registered via oidc, please
|
||||
login via oidc)
|
||||
schema: {}
|
||||
summary: Login
|
||||
tags:
|
||||
- sessions
|
||||
/sessions/{id}:
|
||||
delete:
|
||||
description: Delete a session and logout
|
||||
parameters:
|
||||
- description: The id of the session to delete
|
||||
format: uuid
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.Session'
|
||||
"400":
|
||||
description: Invalid session id
|
||||
schema: {}
|
||||
"401":
|
||||
description: Missing jwt token
|
||||
schema: {}
|
||||
"403":
|
||||
description: Invalid jwt token (or expired)
|
||||
schema: {}
|
||||
"404":
|
||||
description: Session not found with specified id (if not using the /current
|
||||
route)
|
||||
schema: {}
|
||||
security:
|
||||
- Jwt: []
|
||||
summary: Logout
|
||||
tags:
|
||||
- sessions
|
||||
/sessions/current:
|
||||
delete:
|
||||
description: Delete a session and logout
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.Session'
|
||||
"400":
|
||||
description: Invalid session id
|
||||
schema: {}
|
||||
"401":
|
||||
description: Missing jwt token
|
||||
schema: {}
|
||||
"403":
|
||||
description: Invalid jwt token (or expired)
|
||||
schema: {}
|
||||
"404":
|
||||
description: Session not found with specified id (if not using the /current
|
||||
route)
|
||||
schema: {}
|
||||
security:
|
||||
- Jwt: []
|
||||
summary: Logout
|
||||
tags:
|
||||
- sessions
|
||||
/users:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: List all users existing in this instance.
|
||||
parameters:
|
||||
- description: used for pagination.
|
||||
format: uuid
|
||||
in: query
|
||||
name: afterId
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.User'
|
||||
"400":
|
||||
description: Invalid after id
|
||||
schema: {}
|
||||
security:
|
||||
- Jwt:
|
||||
- users.read
|
||||
summary: List all users
|
||||
tags:
|
||||
- users
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Register as a new user and open a session for it
|
||||
parameters:
|
||||
- description: The device the created session will be used on
|
||||
in: query
|
||||
name: device
|
||||
type: string
|
||||
- description: Registration informations
|
||||
in: body
|
||||
name: user
|
||||
schema:
|
||||
$ref: '#/definitions/main.RegisterDto'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/dbc.Session'
|
||||
"400":
|
||||
description: Invalid register body
|
||||
schema: {}
|
||||
"409":
|
||||
description: Duplicated email or username
|
||||
schema: {}
|
||||
summary: Register
|
||||
tags:
|
||||
- users
|
||||
/users/{id}:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Delete an account and all it's sessions.
|
||||
parameters:
|
||||
- description: User id of the user to delete
|
||||
format: uuid
|
||||
in: path
|
||||
name: id
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.User'
|
||||
"404":
|
||||
description: Invalid user id
|
||||
schema: {}
|
||||
security:
|
||||
- Jwt:
|
||||
- users.delete
|
||||
summary: Delete user
|
||||
tags:
|
||||
- users
|
||||
get:
|
||||
description: Get informations about a user from it's id
|
||||
parameters:
|
||||
- description: The id of the user
|
||||
format: uuid
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.User'
|
||||
"404":
|
||||
description: No user with the given id found
|
||||
schema: {}
|
||||
security:
|
||||
- Jwt:
|
||||
- users.read
|
||||
summary: Get user
|
||||
tags:
|
||||
- users
|
||||
/users/me:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Delete your account and all your sessions
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.User'
|
||||
security:
|
||||
- Jwt: []
|
||||
summary: Delete self
|
||||
tags:
|
||||
- users
|
||||
get:
|
||||
description: Get informations about the currently connected user
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/main.User'
|
||||
"401":
|
||||
description: Missing jwt token
|
||||
schema: {}
|
||||
"403":
|
||||
description: Invalid jwt token (or expired)
|
||||
schema: {}
|
||||
security:
|
||||
- Jwt: []
|
||||
summary: Get me
|
||||
tags:
|
||||
- users
|
||||
securityDefinitions:
|
||||
Jwt:
|
||||
in: header
|
||||
name: Authorization
|
||||
type: apiKey
|
||||
Token:
|
||||
in: header
|
||||
name: Authorization
|
||||
type: apiKey
|
||||
swagger: "2.0"
|
49
auth/go.mod
49
auth/go.mod
@ -1,31 +1,46 @@
|
||||
module github.com/zoriya/kyoo/keibi
|
||||
|
||||
go 1.23
|
||||
go 1.23.3
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jackc/pgx/v5 v5.7.2
|
||||
github.com/jackc/pgx/v5 v5.7.3
|
||||
github.com/labstack/echo-jwt/v4 v4.2.0
|
||||
github.com/labstack/echo/v4 v4.13.3
|
||||
github.com/otaxhu/problem v1.3.0
|
||||
github.com/lestrrat-go/jwx v1.2.30
|
||||
github.com/otaxhu/problem v1.4.0
|
||||
github.com/swaggo/echo-swagger v1.4.1
|
||||
github.com/swaggo/swag v1.16.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/otaxhu/type-mismatch-encoding v0.0.0-20241118152201-1861af90dd01 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1
|
||||
github.com/go-playground/validator/v10 v10.25.0
|
||||
github.com/golang-migrate/migrate/v4 v4.18.2
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
|
||||
@ -36,19 +51,19 @@ require (
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.2 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
95
auth/go.sum
95
auth/go.sum
@ -9,8 +9,10 @@ github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6Ct
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dhui/dktest v0.4.3 h1:wquqUxAFdcUgabAVLvSCOKOlag5cIZuaOjYIBOWdsR0=
|
||||
github.com/dhui/dktest v0.4.3/go.mod h1:zNK8IwktWzQRm6I/l2Wjp7MakiyaFWv4G1hjmodmMTs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/dhui/dktest v0.4.4 h1:+I4s6JRE1yGuqflzwqG+aIaMdgXIorCf5P98JnaAWa8=
|
||||
github.com/dhui/dktest v0.4.4/go.mod h1:4+22R4lgsdAXrDyaH4Nqx2JEz2hLp49MqQmm9HLCQhM=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4=
|
||||
@ -21,36 +23,38 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
|
||||
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.2/go.mod h1:2CM6tJvn2kqPXwnXO/d3rAQYiyoIm180VsO8PRX6Rpk=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@ -64,8 +68,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
|
||||
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/pgx/v5 v5.7.3 h1:PO1wNKj/bTAwxSJnO1Z4Ai8j4magtqg2SLNjEDzcXQo=
|
||||
github.com/jackc/pgx/v5 v5.7.3/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@ -82,13 +86,25 @@ github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||
github.com/lestrrat-go/jwx v1.2.30 h1:VKIFrmjYn0z2J51iLPadqoHIVLzvWNa1kCsTqNDHYPA=
|
||||
github.com/lestrrat-go/jwx v1.2.30/go.mod h1:vMxrwFhunGZ3qddmfmEm2+uced8MSI6QFWGTKygjSzQ=
|
||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
@ -101,8 +117,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/otaxhu/problem v1.3.0 h1:w+eJZCbA+p1LBK4SmmX6mECMW1I2Du+yyPZ6QeEWiyI=
|
||||
github.com/otaxhu/problem v1.3.0/go.mod h1:ih/V+2WmZaD5oSCJmhvQAdoKju1xfrgZTv6OiFlXjBY=
|
||||
github.com/otaxhu/problem v1.4.0 h1:Pf4Hgn6bYwisSpW+gG/gVR8//yqPnOkLUJAZ2PX2Pzo=
|
||||
github.com/otaxhu/problem v1.4.0/go.mod h1:KhJVvH7FFARjbo8zD4uhznhuB6OraYy3DT60IZaVdU0=
|
||||
github.com/otaxhu/type-mismatch-encoding v0.0.0-20241118152201-1861af90dd01 h1:fu4zB0bmnJjRL/mAtA8dKCEw4VAYItmrM2Ry5xhqm+o=
|
||||
github.com/otaxhu/type-mismatch-encoding v0.0.0-20241118152201-1861af90dd01/go.mod h1:I7A2jI8mxo2WCUaaDnzo+Bso12DxLs4lERe+R6M09/Y=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@ -111,7 +129,9 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk=
|
||||
@ -138,36 +158,35 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -179,16 +198,16 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
53
auth/jwt.go
53
auth/jwt.go
@ -2,8 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/http"
|
||||
"strings"
|
||||
@ -11,6 +10,7 @@ import (
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
)
|
||||
|
||||
type Jwt struct {
|
||||
@ -18,11 +18,6 @@ type Jwt struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
// The public key used to sign jwt tokens. It can be used by your services to check if the jwt is valid.
|
||||
PublicKey string `json:"publicKey"`
|
||||
}
|
||||
|
||||
// @Summary Get JWT
|
||||
// @Description Convert a session token to a short lived JWT.
|
||||
// @Tags jwt
|
||||
@ -53,40 +48,50 @@ func (h *Handler) CreateJwt(c echo.Context) error {
|
||||
}()
|
||||
|
||||
claims := maps.Clone(session.User.Claims)
|
||||
claims["username"] = session.User.Username
|
||||
claims["sub"] = session.User.Id.String()
|
||||
claims["sid"] = session.Id.String()
|
||||
claims["iss"] = h.config.Issuer
|
||||
claims["iss"] = h.config.PublicUrl
|
||||
claims["iat"] = &jwt.NumericDate{
|
||||
Time: time.Now().UTC(),
|
||||
}
|
||||
claims["exp"] = &jwt.NumericDate{
|
||||
Time: time.Now().UTC().Add(time.Hour),
|
||||
}
|
||||
claims["iss"] = &jwt.NumericDate{
|
||||
Time: time.Now().UTC(),
|
||||
}
|
||||
jwt := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
t, err := jwt.SignedString(h.config.JwtPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Response().Header().Add("Authorization", fmt.Sprintf("Bearer %s", t))
|
||||
return c.JSON(http.StatusOK, Jwt{
|
||||
Token: t,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Info
|
||||
// @Description Get info like the public key used to sign the jwts.
|
||||
// @Summary Jwks
|
||||
// @Description Get the jwks info, used to validate jwts.
|
||||
// @Tags jwt
|
||||
// @Produce json
|
||||
// @Success 200 {object} Info
|
||||
// @Router /info [get]
|
||||
func (h *Handler) GetInfo(c echo.Context) error {
|
||||
key := pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "RSA PUBLIC KEY",
|
||||
Bytes: x509.MarshalPKCS1PublicKey(h.config.JwtPublicKey),
|
||||
},
|
||||
)
|
||||
// @Success 200 {object} jwk.Key
|
||||
// @Router /.well-known/jwks.json [get]
|
||||
func (h *Handler) GetJwks(c echo.Context) error {
|
||||
key, err := jwk.New(h.config.JwtPublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.JSON(200, Info{
|
||||
PublicKey: string(key),
|
||||
key.Set("use", "sig")
|
||||
key.Set("key_ops", "verify")
|
||||
set := jwk.NewSet()
|
||||
set.Add(key)
|
||||
return c.JSON(200, set)
|
||||
}
|
||||
|
||||
func (h *Handler) GetOidcConfig(c echo.Context) error {
|
||||
return c.JSON(200, struct {
|
||||
JwksUri string `json:"jwks_uri"`
|
||||
}{
|
||||
JwksUri: fmt.Sprintf("%s/.well-known/jwks.json", h.config.PublicUrl),
|
||||
})
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ type Validator struct {
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
func (v *Validator) Validate(i interface{}) error {
|
||||
func (v *Validator) Validate(i any) error {
|
||||
if err := v.validator.Struct(i); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
@ -149,7 +149,7 @@ func main() {
|
||||
|
||||
db, err := OpenDatabase()
|
||||
if err != nil {
|
||||
e.Logger.Fatal("Could not open databse: ", err)
|
||||
e.Logger.Fatal("Could not open database: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -184,7 +184,8 @@ func main() {
|
||||
r.DELETE("/sessions/:id", h.Logout)
|
||||
|
||||
g.GET("/jwt", h.CreateJwt)
|
||||
g.GET("/info", h.GetInfo)
|
||||
e.GET("/.well-known/jwks.json", h.GetJwks)
|
||||
e.GET("/.well-known/openid-configuration", h.GetOidcConfig)
|
||||
|
||||
g.GET("/swagger/*", echoSwagger.WrapHandler)
|
||||
|
||||
|
@ -25,5 +25,5 @@ COPY --from=builder /app /app
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
# The back can take a long time to start if meilisearch is initializing
|
||||
HEALTHCHECK --interval=5s --retries=15 CMD curl --fail http://localhost:5000/health || exit
|
||||
HEALTHCHECK --interval=30s --retries=15 CMD curl --fail http://localhost:5000/health || exit
|
||||
ENTRYPOINT ["/app/kyoo"]
|
||||
|
@ -17,6 +17,6 @@ RUN dotnet restore
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
ENV DOTNET_USE_POLLING_FILE_WATCHER 1
|
||||
# HEALTHCHECK --interval=5s CMD curl --fail http://localhost:5000/health || exit
|
||||
# HEALTHCHECK --interval=30s CMD curl --fail http://localhost:5000/health || exit
|
||||
HEALTHCHECK CMD true
|
||||
ENTRYPOINT ["dotnet", "watch", "--non-interactive", "run", "--no-restore", "--project", "/app/src/Kyoo.Core"]
|
||||
|
190
docker-compose.dev-v5.yml
Normal file
190
docker-compose.dev-v5.yml
Normal file
@ -0,0 +1,190 @@
|
||||
x-transcoder: &transcoder-base
|
||||
build:
|
||||
context: ./transcoder
|
||||
dockerfile: Dockerfile.dev
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- transcoder
|
||||
ports:
|
||||
- "7666:7666"
|
||||
restart: on-failure
|
||||
cpus: 1
|
||||
env_file:
|
||||
- ./.env
|
||||
environment:
|
||||
- GOCODER_PREFIX=/video
|
||||
volumes:
|
||||
- ./transcoder:/app
|
||||
- ${LIBRARY_ROOT}:/video:ro
|
||||
- ${CACHE_ROOT}:/cache
|
||||
- transcoder_metadata:/metadata
|
||||
|
||||
services:
|
||||
front:
|
||||
build:
|
||||
context: ./front
|
||||
dockerfile: Dockerfile.dev
|
||||
volumes:
|
||||
- ./front:/app
|
||||
- /app/.yarn
|
||||
- /app/node_modules
|
||||
- /app/apps/mobile/node_modules
|
||||
- /app/apps/web/.next/
|
||||
- /app/apps/mobile/.expo/
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "8081:8081"
|
||||
restart: on-failure
|
||||
environment:
|
||||
- KYOO_URL=${KYOO_URL:-http://api:5000/api}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.front.rule=PathPrefix(`/`)"
|
||||
|
||||
auth:
|
||||
build:
|
||||
context: ./auth
|
||||
dockerfile: Dockerfile.dev
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "4568:4568"
|
||||
env_file:
|
||||
- ./.env
|
||||
environment:
|
||||
- KEIBI_PREFIX=/auth
|
||||
volumes:
|
||||
- ./auth:/app
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.auth.rule=PathPrefix(`/auth/`)"
|
||||
- "traefik.http.routers.auth.rule=PathPrefix(`/.well-known/`)"
|
||||
|
||||
api:
|
||||
build:
|
||||
context: ./api
|
||||
dockerfile: Dockerfile.dev
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
auth:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./api:/app
|
||||
- /app/node_modules
|
||||
ports:
|
||||
- "3567:3567"
|
||||
environment:
|
||||
- KYOO_PREFIX=/api
|
||||
env_file:
|
||||
- ./.env
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.api.rule=PathPrefix(`/api/`)"
|
||||
- "traefik.http.routers.api.middlewares=phantom-token"
|
||||
- "traefik.http.middlewares.phantom-token.forwardauth.address=http://auth:4568/auth/jwt"
|
||||
- "traefik.http.middlewares.phantom-token.forwardauth.authRequestHeaders=Authorization,X-Api-Key"
|
||||
- "traefik.http.middlewares.phantom-token.forwardauth.authResponseHeaders=Authorization"
|
||||
|
||||
# scanner:
|
||||
# build: ./scanner
|
||||
# restart: on-failure
|
||||
# depends_on:
|
||||
# back:
|
||||
# condition: service_healthy
|
||||
# env_file:
|
||||
# - ./.env
|
||||
# environment:
|
||||
# - KYOO_URL=${KYOO_URL:-http://back:5000/api}
|
||||
# volumes:
|
||||
# - ${LIBRARY_ROOT}:/video:ro
|
||||
#
|
||||
# matcher:
|
||||
# build: ./scanner
|
||||
# command: matcher
|
||||
# restart: on-failure
|
||||
# depends_on:
|
||||
# back:
|
||||
# condition: service_healthy
|
||||
# env_file:
|
||||
# - ./.env
|
||||
# environment:
|
||||
# - KYOO_URL=${KYOO_URL:-http://back:5000/api}
|
||||
|
||||
transcoder:
|
||||
<<: *transcoder-base
|
||||
profiles: ['', 'cpu']
|
||||
|
||||
transcoder-nvidia:
|
||||
<<: *transcoder-base
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
driver: cdi
|
||||
device_ids:
|
||||
- nvidia.com/gpu=all
|
||||
environment:
|
||||
- GOCODER_PREFIX=/video
|
||||
- GOCODER_HWACCEL=nvidia
|
||||
profiles: ['nvidia']
|
||||
|
||||
transcoder-vaapi:
|
||||
<<: *transcoder-base
|
||||
devices:
|
||||
- /dev/dri:/dev/dri
|
||||
environment:
|
||||
- GOCODER_PREFIX=/video
|
||||
- GOCODER_HWACCEL=vaapi
|
||||
- GOCODER_VAAPI_RENDERER=${GOCODER_VAAPI_RENDERER:-/dev/dri/renderD128}
|
||||
profiles: ['vaapi']
|
||||
# qsv is the same setup as vaapi but with the hwaccel env var different
|
||||
transcoder-qsv:
|
||||
<<: *transcoder-base
|
||||
devices:
|
||||
- /dev/dri:/dev/dri
|
||||
environment:
|
||||
- GOCODER_PREFIX=/video
|
||||
- GOCODER_HWACCEL=qsv
|
||||
- GOCODER_VAAPI_RENDERER=${GOCODER_VAAPI_RENDERER:-/dev/dri/renderD128}
|
||||
profiles: ['qsv']
|
||||
|
||||
traefik:
|
||||
image: traefik:v3.3
|
||||
restart: on-failure
|
||||
command:
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entryPoints.web.address=:8901"
|
||||
- "--accesslog=true"
|
||||
ports:
|
||||
- "8901:8901"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
|
||||
postgres:
|
||||
image: postgres:15
|
||||
restart: on-failure
|
||||
env_file:
|
||||
- ./.env
|
||||
volumes:
|
||||
- db:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
db:
|
||||
transcoder_metadata:
|
@ -46,5 +46,8 @@ WORKDIR /app
|
||||
ENV NVIDIA_VISIBLE_DEVICES="all"
|
||||
ENV NVIDIA_DRIVER_CAPABILITIES="all"
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
EXPOSE 7666
|
||||
CMD ["wgo", "run", "-race", "."]
|
||||
|
Loading…
x
Reference in New Issue
Block a user