1
Fork 0
mirror of https://github.com/Steffo99/festa.git synced 2024-12-22 14:44:21 +00:00

I actually made a lot of stuff without committing

This commit is contained in:
Steffo 2022-05-24 12:08:02 +02:00
parent 79063b6735
commit fb96441289
Signed by: steffo
GPG key ID: 6965406171929D01
11 changed files with 262 additions and 139 deletions

7
.env
View file

@ -1,7 +0,0 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="postgresql://steffo@localhost/festa?host=/var/run/postgresql"

15
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,15 @@
{
"files.exclude": {
// Version control
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
// Node
"node_modules": true,
".next": true,
"yarn.lock": true,
}
}

View file

@ -13,7 +13,8 @@
"next": "12.1.6",
"prisma": "^3.14.0",
"react": "18.1.0",
"react-dom": "18.1.0"
"react-dom": "18.1.0",
"react-telegram-login": "^1.1.2"
},
"devDependencies": {
"@types/node": "17.0.35",

View file

@ -3,7 +3,7 @@ import type { NextPage } from 'next'
const Page: NextPage = () => {
return (
<div>
sus
wrong page bro
</div>
)
}

47
pages/party/[slug].tsx Normal file
View file

@ -0,0 +1,47 @@
import type { NextPage, NextPageContext } from 'next'
import { useRouter } from 'next/router'
import TelegramLoginButton from 'react-telegram-login'
import * as Telegram from "../../utils/telegram"
interface PageProps {
userData: Telegram.LoginData | null
}
export async function getServerSideProps(context: NextPageContext): Promise<{props: PageProps}> {
const props: PageProps = {
userData: null,
}
const token = process.env.BOT_TOKEN
if(token === undefined) {
throw new Error("BOT_TOKEN is not set on the server-side, cannot perform login validation.")
}
if(context.query.hash !== undefined) {
const loginResponse = new Telegram.LoginResponse(context.query)
if(loginResponse.isRecent() && loginResponse.isValid(process.env.BOT_TOKEN)) {
props.userData = loginResponse.serialize()
}
}
return {props}
}
const Page: NextPage<PageProps> = ({userData}) => {
const router = useRouter()
return (
<div>
{userData ?
JSON.stringify(userData)
:
<TelegramLoginButton
dataAuthUrl={router.asPath}
botName="festaappbot"
/>
}
</div>
)
}
export default Page

View file

@ -32,6 +32,17 @@ model Event {
vehicles Vehicle[]
}
/// A user is a person who is using the Festa website, who logged in via Telegram.
model User {
id BigInt @id
firstName String
lastName String?
username String?
photoUrl String?
lastAuthDate DateTime
hash String
}
/// A partecipant is a person who may or may not partecipate to the event.
model Partecipant {
id Int @id @default(autoincrement())

View file

@ -1,116 +0,0 @@
.container {
padding: 0 2rem;
}
.main {
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer {
display: flex;
flex: 1;
padding: 2rem 0;
border-top: 1px solid #eaeaea;
justify-content: center;
align-items: center;
}
.footer a {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
margin: 4rem 0;
line-height: 1.5;
font-size: 1.5rem;
}
.code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
}
.card {
margin: 1rem;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
max-width: 300px;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
.logo {
height: 1em;
margin-left: 0.5rem;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}

View file

@ -1,16 +1,22 @@
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
html {
padding: 0;
margin: 0;
}
* {
box-sizing: border-box;
box-sizing: border-box;
}
@media (prefers-color-scheme: light) {
body {
background-color: white;
color: black;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: black;
color: white;
}
}

View file

@ -13,7 +13,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]

150
utils/telegram.ts Normal file
View file

@ -0,0 +1,150 @@
import nodecrypto from "crypto"
/**
* The validated user data serialized by the server.
*/
export interface LoginData {
id: number
first_name: string
last_name: string | null
username: string | null
photo_url: string | null
lang: string | null
}
/**
* The response sent by Telegram after a login.
*/
export class LoginResponse {
id: number
first_name: string
last_name?: string
username?: string
photo_url?: string
auth_date: number
hash: string
lang?: string
/**
* Construct a new {@link LoginResponse} from a query string object as returned by Next.js.
*
* @param queryObj The query string object, from `context.query`.
*/
constructor(queryObj: {[_: string]: string | string[]}) {
if(typeof queryObj.id === "object") {
throw new Error("Multiple `id` parameters specified in the query string, cannot construct LoginResponse.")
}
if(typeof queryObj.first_name === "object") {
throw new Error("Multiple `first_name` parameters specified in the query string, cannot construct LoginResponse.")
}
if(typeof queryObj.last_name === "object") {
throw new Error("Multiple `last_name` parameters specified in the query string, cannot construct LoginResponse.")
}
if(typeof queryObj.username === "object") {
throw new Error("Multiple `username` parameters specified in the query string, cannot construct LoginResponse.")
}
if(typeof queryObj.photo_url === "object") {
throw new Error("Multiple `photo_url` parameters specified in the query string, cannot construct LoginResponse.")
}
if(typeof queryObj.auth_date === "object") {
throw new Error("Multiple `auth_date` parameters specified in the query string, cannot construct LoginResponse.")
}
if(typeof queryObj.hash === "object") {
throw new Error("Multiple `hash` parameters specified in the query string, cannot construct LoginResponse.")
}
if(typeof queryObj.lang === "object") {
throw new Error("Multiple `hash` parameters specified in the query string, cannot construct LoginResponse.")
}
this.id = parseInt(queryObj.id)
this.first_name = queryObj.first_name
this.last_name = queryObj.last_name
this.username = queryObj.username
this.photo_url = queryObj.photo_url
this.auth_date = parseInt(queryObj.auth_date)
this.hash = queryObj.hash
this.lang = queryObj.lang
}
/**
* Serialize this response into a {@link LoginData} object, which can be passed to the client by Next.js.
*
* @returns The {@link LoginData} object.
*/
serialize(): LoginData {
return {
id: this.id ?? null,
first_name: this.first_name ?? null,
last_name: this.last_name ?? null,
username: this.username ?? null,
photo_url: this.photo_url ?? null,
lang: this.lang ?? null,
}
}
/**
* Stringify a {@link LoginResponse} in [the format required to verify a Telegram Login](https://core.telegram.org/widgets/login#checking-authorization).
*
* @param data The data to encode.
* @returns The stringified data.
*/
stringify(): string {
const string = Object.entries(this)
.filter(([key, _]) => key !== "hash")
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => `${key}=${value}`)
.sort()
.join("\n")
return string
}
/**
* Check if the `auth_date` of the response is recent: it must be in the past, but within `maxSeconds` from the current date.
*
* @param maxSeconds The maximum number of milliseconds that may pass after authentication for the response to be considered valid; defaults to `300000`, 5 minutes.
* @returns `true` if the response can be considered recent, `false` otherwise.
*/
isRecent(maxSeconds: number = 300000): boolean {
const diff = new Date().getTime() - new Date(this.auth_date * 1000).getTime()
return 0 < diff && diff <= maxSeconds
}
/**
* Calculate the "`hash`" of a Telegram Login using [this procedure](https://core.telegram.org/widgets/login#checking-authorization).
*
* @param token The bot token used to validate the signature.
* @returns The calculated value of the `hash` {@link LoginResponse} parameter.
*/
hmac(token: string): string {
const key = hashToken(token)
const hmac = nodecrypto.createHmac("sha256", key)
hmac.update(this.stringify())
return hmac.digest("hex")
}
/**
* Validate a Telegram Login using [this procedure](https://core.telegram.org/widgets/login#checking-authorization).
*
* @param token The bot token used to validate the signature.
* @returns `true` if the validation is successful, `false` otherwise.
*/
isValid(token: string): boolean {
const client = this.hmac(token)
const server = this.hash
return client === server
}
}
/**
* Hash a Telegram bot token using SHA-256.
*
* @param token The bot token to hash.
* @returns The hex digest of the hash.
*/
function hashToken(token: string): Buffer {
const hash = nodecrypto.createHash("sha256")
hash.update(token)
return hash.digest()
}

View file

@ -1406,7 +1406,7 @@ prisma@^3.14.0:
dependencies:
"@prisma/engines" "3.14.0-36.2b0c12756921c891fec4f68d9444e18c7d5d4a6a"
prop-types@^15.8.1:
prop-types@^15.6.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -1438,6 +1438,13 @@ react-is@^16.13.1:
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-telegram-login@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/react-telegram-login/-/react-telegram-login-1.1.2.tgz#28b9bdd68bb2710afca19354ac1f9428092836f0"
integrity sha512-pDP+bvfaklWgnK5O6yvZnIwgky0nnYUU6Zhk0EjdMSkPsLQoOzZRsXIoZnbxyBXhi7346bsxMH+EwwJPTxClDw==
dependencies:
react "^16.13.1"
react@18.1.0:
version "18.1.0"
resolved "https://registry.npmjs.org/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890"
@ -1445,6 +1452,15 @@ react@18.1.0:
dependencies:
loose-envify "^1.1.0"
react@^16.13.1:
version "16.14.0"
resolved "https://registry.npmjs.org/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"