Skip to content
Merged

2.0.0 #243

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions apps/api/email/registration-successful.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
Vielen Dank für deine Anmeldung zur Veranstaltung {{ veranstaltung }}.
</mj-text>

{{#if accessToken}}
<mj-text
font-size="16px"
line-height="1.5"
>
<span>Bitte lade im nächsten Schritt ein Bild für {{ name }} hoch:&nbsp;</span>
<a
href="https://{{ hostname }}/ausschreibung/{{ unterveranstaltungId }}/anmeldung/{{ anmeldungId }}/foto-upload?accessToken={{ accessToken }}"
>Hier Foto hochladen</a
>
</mj-text>
{{/if}}

<mj-text
font-size="16px"
line-height="1.5"
Expand All @@ -33,6 +46,32 @@
deinem Account zuzuordnen.
</mj-text>

<mj-text
font-size="16px"
line-height="1.5"
>
<p>Dazu befolgt du einfach die folgenden Schritte:</p>

<ol>
<li>
Du erstellst dir einen Account auf
<a
href="https://{{ hostname }}/registrierung"
rel="noreferrer"
>{{ hostname }}</a
>
</li>
<li>
Du gibst den unten genannten <b>Zuordnungscode</b> auf der folgenden Seite ein:
<a
href="https://{{ hostname }}/my/anmeldung-zuordnen"
rel="noreferrer"
>Anmeldung per Code zuordnen</a
>
</li>
</ol>
</mj-text>

<mj-text
font-size="16px"
line-height="1.5"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Anmeldung" ADD COLUMN "accessToken" UUID;
1 change: 1 addition & 0 deletions apps/api/prisma/schema/Anmeldung.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ model Anmeldung {
createdAt DateTime @default(now())
status AnmeldungStatus @default(OFFEN)
comment String?
accessToken String? @db.Uuid
assignmentCode String? @db.Uuid

unterveranstaltungId Int
Expand Down
2 changes: 1 addition & 1 deletion apps/api/prisma/seeders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type Seeder = (prisma: PrismaClient) => Promise<void>

const seeders: Seeder[] = (() => {
// in produktion nur Gliederungen importieren
if (isProduction()) {
if (isProduction() || process.env.DISABLE_SEEDER === '1') {
return [importGliederungen]
}

Expand Down
1 change: 0 additions & 1 deletion apps/api/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ app.use(
'img-src': ["'self'", '*.githubusercontent.com', 'data:', 'dlrgbrahmseedigitalprod.blob.core.windows.net'],
'connect-src': ["'self'", 'dlrgbrahmseedigitalprod.blob.core.windows.net'],
},
reportOnly: true,
},
})
)
Expand Down
6 changes: 5 additions & 1 deletion apps/api/src/services/anmeldung/anmeldung.router.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Prettier ignored is because this file is generated
import { mergeRouters } from '../../trpc.js'
import { anmeldungAccessTokenValidateProcedure } from './anmeldungAccessTokenValidate.js'
import { anmeldungFotoUploadProcedure } from './anmeldungFotoUpload.js'

import { anmeldungGetProcedure } from './anmeldungGet.js'
import { anmeldungGliederungPatchProcedure } from './anmeldungGliederungPatch.js'
Expand All @@ -26,6 +28,8 @@ export const anmeldungRouter = mergeRouters(
anmeldungCountProcedure.router,
anmeldungListProcedure.router,
anmeldungGetProcedure.router,
anmeldungZuordnenProcedure.router
anmeldungZuordnenProcedure.router,
anmeldungAccessTokenValidateProcedure.router,
anmeldungFotoUploadProcedure.router
// Add Routes here - do not delete this line
)
44 changes: 44 additions & 0 deletions apps/api/src/services/anmeldung/anmeldungAccessTokenValidate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import z from 'zod'

import prisma from '../../prisma.js'
import { definePublicQueryProcedure } from '../../types/defineProcedure.js'

export const anmeldungAccessTokenValidateProcedure = definePublicQueryProcedure({
key: 'accessTokenValidate',
inputSchema: z.strictObject({
unterveranstaltungId: z.number().int(),
anmeldungId: z.number().int(),
accessToken: z.string().uuid(),
}),
handler: ({ input: { unterveranstaltungId, anmeldungId, accessToken } }) =>
prisma.anmeldung.findFirst({
where: {
unterveranstaltungId,
id: anmeldungId,
accessToken,
},
select: {
person: {
select: {
id: true,
firstname: true,
lastname: true,
},
},
unterveranstaltung: {
select: {
veranstaltung: {
select: {
name: true,
},
},
gliederung: {
select: {
name: true,
},
},
},
},
},
}),
})
32 changes: 32 additions & 0 deletions apps/api/src/services/anmeldung/anmeldungFotoUpload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import z from 'zod'

import prisma from '../../prisma.js'

import { definePublicMutateProcedure } from '../../types/defineProcedure.js'

export const anmeldungFotoUploadProcedure = definePublicMutateProcedure({
key: 'anmeldungFotoUpload',
inputSchema: z.strictObject({
unterveranstaltungId: z.number().int(),
anmeldungId: z.number().int(),
accessToken: z.string().uuid(),
fileId: z.string(),
}),
handler: async ({ input: { unterveranstaltungId, anmeldungId, accessToken, fileId } }) => {
await prisma.anmeldung.update({
where: {
unterveranstaltungId,
id: anmeldungId,
accessToken,
},
data: {
accessToken: null,
person: {
update: {
photoId: fileId,
},
},
},
})
},
})
5 changes: 5 additions & 0 deletions apps/api/src/services/anmeldung/anmeldungPublicCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export async function handle({ ctx, input, isPublic }: HandleProps) {
},
})

const accessToken = randomUUID()
const assignmentCode = ctx.authenticated ? null : randomUUID()
const anmeldung = await prisma.anmeldung.create({
data: {
Expand All @@ -90,6 +91,7 @@ export async function handle({ ctx, input, isPublic }: HandleProps) {
customFieldValues: {
createMany: customFieldValuesCreateMany(input.customFieldValues),
},
accessToken,
assignmentCode,
},
})
Expand Down Expand Up @@ -119,7 +121,10 @@ export async function handle({ ctx, input, isPublic }: HandleProps) {
gliederung: unterveranstaltung.gliederung.name,
veranstaltung: unterveranstaltung.veranstaltung.name,
hostname: unterveranstaltung.veranstaltung.hostname!.hostname,
unterveranstaltungId: unterveranstaltung.id,
anmeldungId: anmeldung.id,
assignmentCode,
accessToken,
},
})
}
Expand Down
31 changes: 31 additions & 0 deletions apps/api/src/services/file/anmeldungPublicFotoUpload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { z } from 'zod'
import { definePublicMutateProcedure } from '../../types/defineProcedure.js'
import { fileCreateSchema, handleFileUpload } from './fileCreate.js'
import client from '../../prisma.js'
import { TRPCError } from '@trpc/server'

export const anmeldungPublicFotoUploadProcedure = definePublicMutateProcedure({
key: 'anmeldungPublicFotoUpload',
inputSchema: fileCreateSchema.extend({
unterveranstaltungId: z.number().int(),
anmeldungId: z.number().int(),
accessToken: z.string().uuid(),
}),
handler: async ({ input: { unterveranstaltungId, anmeldungId, accessToken, mimetype } }) => {
const anmeldung = await client.anmeldung.findFirst({
where: {
unterveranstaltungId,
id: anmeldungId,
accessToken,
},
})

if (anmeldung === null) {
throw new TRPCError({
code: 'UNAUTHORIZED',
})
}

return await handleFileUpload({ mimetype })
},
})
4 changes: 3 additions & 1 deletion apps/api/src/services/file/file.router.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Prettier ignored is because this file is generated
import { mergeRouters } from '../../trpc.js'
import { anmeldungPublicFotoUploadProcedure } from './anmeldungPublicFotoUpload.js'

import { fileCreateProcedure } from './fileCreate.js'
import { fileGetUrlActionProcedure } from './fileGetUrl.js'
// Import Routes here - do not delete this line

export const fileRouter = mergeRouters(
fileCreateProcedure.router,
fileGetUrlActionProcedure.router
fileGetUrlActionProcedure.router,
anmeldungPublicFotoUploadProcedure.router
// Add Routes here - do not delete this line
)
86 changes: 45 additions & 41 deletions apps/api/src/services/file/fileCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,52 @@ import config from '../../config.js'
import prisma from '../../prisma.js'
import { defineProtectedMutateProcedure } from '../../types/defineProcedure.js'

export const fileCreateSchema = z.strictObject({
mimetype: z.string(),
})

export async function handleFileUpload(input: z.infer<typeof fileCreateSchema>) {
const provider = config.fileDefaultProvider
let key: string = randomUUID()
if (provider === 'AZURE') {
key = `${config.fileProviders.AZURE.folder}/${key}`
}

const file = await prisma.file.create({
data: {
provider: provider,
key: key,
mimetype: input.mimetype,
},
select: {
id: true,
provider: true,
uploaded: true,
},
})

let azureUploadUrl: string | null = null
if (file.provider === 'AZURE' && azureStorage !== null) {
const containerClient = azureStorage.getContainerClient(config.fileProviders.AZURE.container)
const blockBlobClient = containerClient.getBlockBlobClient(key)
const permissions = BlobSASPermissions.from({ read: true, write: true, add: true, create: true })
azureUploadUrl = await blockBlobClient.generateSasUrl({
startsOn: dayjs().subtract(5, 'minute').toDate(),
expiresOn: dayjs().add(20, 'minute').toDate(),
permissions: permissions,
contentType: input.mimetype,
})
}

return {
...file,
azureUploadUrl,
}
}

export const fileCreateProcedure = defineProtectedMutateProcedure({
key: 'fileCreate',
roleIds: ['ADMIN', 'GLIEDERUNG_ADMIN'],
inputSchema: z.strictObject({
mimetype: z.string(),
}),
async handler(options) {
const provider = config.fileDefaultProvider
let key: string = randomUUID()
if (provider === 'AZURE') {
key = `${config.fileProviders.AZURE.folder}/${key}`
}

const file = await prisma.file.create({
data: {
provider: provider,
key: key,
mimetype: options.input.mimetype,
},
select: {
id: true,
provider: true,
uploaded: true,
},
})

let azureUploadUrl: string | null = null
if (file.provider === 'AZURE' && azureStorage !== null) {
const containerClient = azureStorage.getContainerClient(config.fileProviders.AZURE.container)
const blockBlobClient = containerClient.getBlockBlobClient(key)
const permissions = BlobSASPermissions.from({ read: true, write: true, add: true, create: true })
azureUploadUrl = await blockBlobClient.generateSasUrl({
startsOn: dayjs().subtract(5, 'minute').toDate(),
expiresOn: dayjs().add(20, 'minute').toDate(),
permissions: permissions,
contentType: options.input.mimetype,
})
}

return {
...file,
azureUploadUrl,
}
},
inputSchema: fileCreateSchema,
handler: ({ input }) => handleFileUpload(input),
})
2 changes: 1 addition & 1 deletion apps/api/src/types/defineCustomFieldValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function defineCustomFieldValues() {
return z.array(
z.strictObject({
fieldId: z.number().int(),
value: z.union([z.string(), z.boolean(), z.undefined()]),
value: z.union([z.string(), z.boolean(), z.undefined(), z.number()]),
})
)
}
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"clsx": "^2.1.1",
"http2-proxy": "^5.0.53",
"intl-tel-input": "^24.4.0",
"primevue": "^4.2.5",
"radix-vue": "^1.9.5",
"remixicon": "^3.5.0",
"simple-syntax-highlighter": "^3.1.1",
Expand Down
Loading
Loading