1674815520
The open-source Calendly alternative.
Scheduling infrastructure for absolutely everyone
The open source Calendly alternative. You are in charge of your own data, workflow and appearance.
Calendly and other scheduling tools are awesome. It made our lives massively easier. We're using it for business meetings, seminars, yoga classes and even calls with our families. However, most tools are very limited in terms of control and customisations.
That's where Cal.com comes in. Self-hosted or hosted by us. White-label by design. API-driven and ready to be deployed on your own domain. Full control of your events and data.
Cal officially launched as v.1.0 on 15th of September, however a lot of new features are coming. Watch releases of this repository to be notified for future updates:
To get a local copy up and running, please follow these simple steps.
Here is what you need to be able to run Cal.
If you want to enable any of the available integrations, you may want to obtain additional credentials for each one. More details on this can be found below under the integrations section.
Clone the repo into a public GitHub repository (or fork https://github.com/calcom/cal.com/fork). If you plan to distribute the code, keep the source code public to comply with AGPLv3. To clone in a private repository, acquire a commercial license)
git clone https://github.com/calcom/cal.com.git
Go to the project folder
cd cal.com
Install packages with yarn
yarn
Set up your .env file
.env.example
to .env
openssl rand -base64 32
to generate a key and add it under NEXTAUTH_SECRET
in the .env file.openssl rand -base64 24
to generate a key and add it under CALENDSO_ENCRYPTION_KEY
in the .env file.yarn dx
- Requires Docker and Docker Compose to be installed
- Will start a local Postgres instance with a few test users - the credentials will be logged in the console
yarn dx
Add
NEXT_PUBLIC_DEBUG=1
anywhere in your.env
to get logging information for all the queries and mutations driven by trpc.
echo 'NEXT_PUBLIC_DEBUG=1' >> .env
Configure environment variables in the .env
file. Replace <user>
, <pass>
, <db-host>
, <db-port>
with their applicable values
DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>'
Set a 32 character random string in your .env file for the CALENDSO_ENCRYPTION_KEY
(You can use a command like openssl rand -base64 24
to generate one).
Set up the database using the Prisma schema (found in packages/prisma/schema.prisma
)
yarn workspace @calcom/prisma db-deploy
Run (in development mode)
yarn dev
Open Prisma Studio to look at or modify the database content:
yarn db-studio
Click on the User
model to add a new user record.
Fill out the fields email
, username
, password
, and set metadata
to empty {}
(remembering to encrypt your password with BCrypt) and click Save 1 Record
to create your first user.
New users are set on a
TRIAL
plan by default. You might want to adjust this behavior to your needs in thepackages/prisma/schema.prisma
file.
Open a browser to http://localhost:3000 and login with your just created, first user.
Be sure to set the environment variable NEXTAUTH_URL
to the correct value. If you are running locally, as the documentation within .env.example
mentions, the value should be http://localhost:3000
.
# In a terminal just run:
yarn test-e2e
# To open last HTML report run:
yarn playwright show-report test-results/reports/playwright-html-report
Pull the current version:
git pull
Check if dependencies got added/updated/removed
yarn
Apply database migrations by running one of the following commands:
In a development environment, run:
yarn workspace @calcom/prisma db-migrate
(this can clear your development database in some cases)
In a production environment, run:
yarn workspace @calcom/prisma db-deploy
Check for .env
variables changes
yarn predev
Start the server. In a development environment, just do:
yarn dev
For a production build, run for example:
yarn build
yarn start
Enjoy the new version.
The Docker configuration for Cal is an effort powered by people within the community.
If you want to contribute to the Docker repository, reply here.
The Docker configuration can be found in our docker repository.
Issues with Docker? Find your answer or open a new discussion here to ask the community.
Cal.com, Inc. does not provide official support for Docker, but we will accept fixes and documentation. Use at your own risk.
You can deploy Cal on Railway using the button above. The team at Railway also have a detailed blog post on deploying Cal on their platform.
Currently Vercel Pro Plan is required to be able to Deploy this application with Vercel, due to limitations on the number of serverless functions on the free plan.
See the roadmap project for a list of proposed features (and known issues). You can change the view to see planned tagged releases.
Please see our contributing guide.
We have a list of help wanted that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.
Don't code but still want to contribute? Join our slack and join the #i18n channel and let us know what language you want to translate.
.../auth/calendar.events
, .../auth/calendar.readonly
and select Update.<Cal.com URL>/api/integrations/googlecalendar/callback
and <Cal.com URL>/api/auth/callback/google
replacing Cal.com URL with the URI at which your application runs.After adding Google credentials, you can now Google Calendar App to the app store. You can repopulate the App store by running
cd packages/prisma
yarn seed-app-store
You will need to complete a few more steps to activate Google Calendar App. Make sure to complete section "Obtaining the Google API Credentials". After the do the following
<Cal.com URL>/api/auth/callback/google
<Cal.com URL>/api/integrations/office365calendar/callback
replacing Cal.com URL with the URI at which your application runs.ZOOM_CLIENT_ID
and ZOOM_CLIENT_SECRET
fields.<Cal.com URL>/api/integrations/zoomvideo/callback
replacing Cal.com URL with the URI at which your application runs.meeting:write
.DAILY_API_KEY
field in your .env file.DAILY_SCALE_PLAN
variable to true
in order to use features like video recording.HUBSPOT_CLIENT_ID
and HUBSPOT_CLIENT_SECRET
fields.<Cal.com URL>/api/integrations/hubspot/callback
replacing Cal.com URL with the URI at which your application runs.crm.objects.contacts
Special thanks to these amazing projects which help power Cal.com:
Cal.com is an open startup and Jitsu (an open-source Segment alternative) helps us to track most of the usage metrics.
Author: Calcom
Source Code: https://github.com/calcom/cal.com
License: View license
#opensource #typescript #nextjs #postgresql #prisma #tailwindcss
1672151100
An open source application built using the new router, server components and everything new in Next.js 13.
Right now, I'm using this project as an experiment to see how a modern app (with features like authentication, subscriptions, API routes, static pages for docs ...etc) would work in Next.js 13 and server components.
I'll be posting updates and issues here.
A few people have asked me to turn this into a starter. I think we could do that once the new features are out of beta.
Warning This app is using the canary releases for Next.js 13 and React 18. The new router and app dir is still in beta and not production-ready. NextAuth.js, which is used for authentication, is also not fully supported in Next.js 13 and RSC. Expect some performance hits when testing the dashboard. If you see something broken, you can ping me @shadcn.
/app
dir,A list of things not working right now:
I might add this later. For now, I want to see how far we can get using Next.js only.
If you have some suggestions, feel free to create an issue.
Install dependencies using pnpm:
pnpm install
Copy .env.example
to .env.local
and update the variables.
cp .env.example .env.local
Start the development server:
pnpm dev
Warning This app is a work in progress. I'm building this in public. You can follow the progress on Twitter @shadcn. See the roadmap below.
Author: Shadcn
Source Code: https://github.com/shadcn/taxonomy
License: MIT license
1671161813
Getting started with Prisma ORM, Next.js as full-stack app & Postgres running in a Docker container.
The heroku postgres is the free formula: https://prisma-next-postgres.vercel.app/
free avatar for testing: https://pravatar.cc/
github: https://github.com/daniellaera/prisma-next-postgres
Should You Run Your Database in Docker?: https://vsupalov.com/database-in-docker/
Subscribe: https://www.youtube.com/@daniellaerachannel/featured
1671027840
In this Prisma article, we will learn about How to Create a Quick App using Prisma. Prisma is an ORM (object–relational mapping) tool for Node.js using TypeScript. The software integrates with many of the most popular databases today, including MySQL, SQL Server, SQLite, and MongoDB, and emphasizes a human-readable schema with a type-safe database client. Prisma also includes other features such as migrations, seed data, and a virtual database browser.
In this project, you will use Prisma to connect your Express application to a database server. You will build a schema to model a workout tracker application. Then you’ll create some seed data and use Prisma to run migrations and seed your database. Finally, you’ll create the web application using Pug and Tailwind CSS to build the application frontend.
Prerequisites
This tutorial uses the following technologies but doesn’t require any prior experience:
If you’d like to skip the tutorial and check out the fully built project, you can go view it on GitHub.
Create a new directory for your application. Use the cd
command to navigate to that folder.
Before you begin, you’ll need a free Okta developer account. Install the Okta CLI and run okta register
to sign up for a new account. If you already have an account, run okta login
. Then, run okta apps create
. Select the default app name, or change it as you see fit. Choose Web and press Enter.
Select Other. Then, change the Redirect URI to http://localhost:3000/authorization-code/callback
and use http://localhost:3000/
for the Logout Redirect URI.
What does the Okta CLI do?
The Okta CLI will create an OIDC Web App in your Okta Org. It will add the redirect URIs you specified and grant access to the Everyone group. You will see output like the following when it’s finished:
Okta application configuration has been written to: /path/to/app/.okta.env
Run cat .okta.env
(or type .okta.env
on Windows) to see the issuer and credentials for your app.
export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"
Your Okta domain is the first part of your issuer, before /oauth2/default
.
NOTE: You can also use the Okta Admin Console to create your app. See Create a Web App for more information.
Next, you will use the express-generator
application generator tool to quickly scaffold your application. Run the following command.
npx express-generator@4.16 --view=pug
Now you can install your packages.
npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
@prisma/client
is used to access the database from your server code.dotenv
reads configuration settings from .env
files like the one produced by the Okta CLI.passport
is a middleware for Node.js that is flexible enough to handle most authentication scenarios, including Okta. passport-openidconnect
is a module for passport that lets you authenticate with OpenID Connect.express-session
is required for passport. Express apps use this package for session support. Your application must intiialize session support to use passport.Tailwind CSS
is the CSS framework you will use. If you’ve never used Tailwind before, you may wonder why it’s a development dependency. This is because Tailwind will dynamically build your CSS files from your views and configuration. More on that later.prisma
is the ORM you are using. This is a dev dependency because the Prisma library handles the migrations, seed data, etc., while the @prisma/client
package is used in your application at runtime.At this point, you will initialize Tailwind CSS and Prisma. You will configure them for usage later on.
Start with Prisma and run the following command.
npx prisma init
This command will add a new folder named prisma
to your application. It also adds a file called .env
to your project with some default configuration. Replace the contents of that file with the code below.
# 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="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}
In this example, I used SQLite because it’s easy and compact. But one of the cool things about Prisma and most ORMs is that they can support many databases. The boilerplate code includes comments about configuring your application for other database servers. Feel free to use what you feel most comfortable with and use the appropriate connection string.
Your seed data will use the SEED_USER_NAME
setting. You want to ensure that it’s the same as the Okta username with which you sign in. This will allow the application to associate your logged-in user with the data you will seed into your database.
Next, update your package.json
file with the following code.
{
"name": "workout-app",
"version": "0.0.0",
"private": true,
"prisma": {
"seed": "node prisma/seed.js"
},
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"@prisma/client": "^3.13.0",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^16.0.0",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"passport": "^0.5.2",
"passport-openidconnect": "^0.1.1",
"pug": "2.0.0-beta11"
},
"devDependencies": {
"prisma": "^3.13.0",
"tailwindcss": "^3.0.24"
}
}
This will add the prisma seed
command you need later.
Now you can initialize Tailwind CSS.
npx tailwindcss init
You’ve now added a new file called tailwind.config.js
to the root of your application. Replace the contents of that file with the code below.
module.exports = {
content: ["./views/**/*.pug"],
theme: {
extend: {},
},
plugins: [],
}
In this step, you tell Tailwind CSS where to find the classes you used in your application. You want Tailwind to look in the .pug
files in your views
directory. Tailwind CSS is highly extensible, as seen by the configuration object’s theme
and plugins
settings. A deep dive into this is out of the scope of this article, but I encourage you to look into Tailwind CSS’s site for more information.
The next task is to create the database for your application. Steps will involve writing the schema, writing some seed data, creating the migration, and applying the migration, which will also seed your data.
The prisma init
task from above should have added a file called schema.prisma
to your prisma
directory. Replace the code there with the code below.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "{yourDatabaseProvider}"
url = env("DATABASE_URL")
}
model WorkoutLog {
id Int @id @default(autoincrement())
userId Int
exercise String
amount Decimal
units String
date DateTime
minutes Int
calories Int
user User @relation(fields: [userId], references: [id])
}
model User {
id Int @id @default(autoincrement())
username String @unique
workoutLogs WorkoutLog[]
}
Make sure you replace {yourDatabaseProvider}
with the provider you are using. As you can see, this file defines the User
and WorkoutLog
objects that you will store in the database. The WorkoutLog
has basic information about the user’s workout on a specific day and then associates that record with the User
object. You can also define keys, constraints, and indices from this file. Note the user
property on the WorkoutLog
has a relation defined by the userId
to the id
of the User
table.
Add a file to the prisma
directory named seed.js
and add the following code to it.
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
require("dotenv").config();
async function main() {
const user = await prisma.user.upsert({
where: { username: process.env.SEED_USER_NAME },
update: {},
create: {
username: process.env.SEED_USER_NAME,
workoutLogs: {
create: [
{
exercise: "Running",
amount: 1,
units: "Miles",
date: new Date(2022, 1, 1),
minutes: 8,
calories: 100
},
{
exercise: "Running",
amount: 1.2,
units: "Miles",
date: new Date(2022, 1, 3),
minutes: 10,
calories: 120
},
{
exercise: "Running",
amount: 1.5,
units: "Miles",
date: new Date(2022, 1, 5),
minutes: 12,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 1),
minutes: 15,
calories: 100
},
{
exercise: "Heavy Bag",
amount: 6,
units: "Rounds",
date: new Date(2022, 1, 3),
minutes: 22,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 5),
minutes: 15,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 10,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 10,
calories: 100
},
],
},
},
});
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Prisma knows to use the seed.js
file from the command you added to package.json
. This file will insert the data when you run that command.
You can now add and apply the migration using the Prisma CLI. From the root of your application directory, run the following command.
npx prisma migrate dev --name init
The CLI has several ways to add and apply migrations. I suggest you understand the best way to manage migrations for your environment. The method above is the easiest and fastest way to prepare your database. As part of the migration process, this command will look for the seed
command from your package.json
and run that as well. Once complete, your database should be ready with a database full of seed data to work with.
Now that your database is ready, you can turn your attention to the core of your application.
First, add a new file in the root of your application called ensureLoggedIn.js
and add the following code to it.
function ensureLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login')
}
module.exports = ensureLoggedIn;
This little piece of middleware will make sure the user is authenticated. If they aren’t, you will redirect them to the login
route, which you will configure to use Okta. Otherwise, you will allow the user to reach the next screen.
Next, you can update your routes in the routes
directory. First, remove users.js
as you won’t be using that. Replace the code in index.js
with the following.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});
module.exports = router;
Here you will pass a property for isAuthenticated
so your layout page can properly display a login or logout button.
Next, add a file in the routes
directory for dashboard.js
with the following code.
var express = require("express");
var router = express.Router();
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
const ensureLoggedIn = require("../ensureLoggedIn");
/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
const username = req.user.username;
const dbUser = await prisma.user.findUnique({
where: {
username: username,
},
include: {
workoutLogs: true,
},
});
res.render("dashboard", {
title: "The Workout Tracker - Dashboard",
isAuthenticated: req.isAuthenticated(),
user: dbUser
});
});
module.exports = router;
There’s a lot of magic in this route. First, you are using the ensureLoggedIn
middleware to protect the route. Then you can extract the username from the request and look up the user in the database using the Prisma client. Since the user has a property for its workout logs, you can pass the user as the model and parse the logs on the pug view you will create. This will give view access to the user’s name as well. Of course, you could query the workoutLogs
table directly and include features such as pagination and search if this approach better fits your workflow.
Now, replace the code in app.js
with the following.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');
const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');
var app = express();
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient();
require('dotenv').config({path:'./.okta.env'});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(session({
secret: 'CanYouLookTheOtherWay',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;
// set up passport
passport.use('oidc', new Strategy({
issuer: OKTA_OAUTH2_ISSUER,
authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
clientID: OKTA_OAUTH2_CLIENT_ID,
clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/authorization-code/callback',
scope: 'openid profile'
}, (issuer, profile, done) => {
return done(null, profile);
}));
passport.serializeUser((user, next) => {
next(null, user);
});
passport.deserializeUser((obj, next) => {
next(null, obj);
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/login', passport.authenticate('oidc'));
app.use('/authorization-code/callback',
passport.authenticate('oidc', { failureRedirect: '/error' }),
(req, res, next) => {
res.redirect('/dashboard');
}
);
app.post('/logout', (req, res) => {
req.logout();
req.session.destroy();
res.redirect('/');
});
app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
You updated your routes to properly use the dashboard
routes as well as any login or logout routes that Okta will use. You also set up passport to use Okta using the values the Okta CLI produced in the .okta.env
file.
By default, express-generator
will add a few views for you. You will need to edit them and add a new one of your own. Start by opening the layout.pug
file in the views
directory and replacing the code there with the following.
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/output.css')
body
nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
.w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
.text-sm(class='lg:flex-grow')
div
if(isAuthenticated)
form(action="logout" method="POST")
button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
else
a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
block content
You’ll notice a couple of things about this file. First, the classes are very specific and plentiful. This is typical of Tailwind CSS. Rather than defining how a specific element looks, Tailwind CSS strives to define how a particular style looks, then you can add and remove styles from elements to fit the desired look.
The other thing you’ll see is the Login/Logout button that will change based on the isAuthenticated
property.
Next, your can update index.pug
with the following.
extends layout
block content
.py-12.bg-white
.max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
div.c(class='lg:text-center')
p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
| A small demo built with
a(href="https://expressjs.com/") Express on
a(href="https://nodejs.org/en/") Node.JS
| Secured with
a(href="https://developer.okta.com/signup") Okta.
br
| This app uses
a(href="https://tailwindcss.com/") Tailwind CSS
| for its CSS framework and
a(href="https://www.prisma.io/") Prisma.JS
| for its ORM.
There’s not much to talk about here. Just a splash page with some more Tailwind CSS and some links to the technologies used.
Finally, add a file for dashboard.pug
with the following code.
extends layout
block content
.flex.flex-wrap
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-green-600
i.fa.fa-wallet.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0)
span.text-green-500
i.fas.fa-caret-up
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-pink-600
i.fas.fa-users.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Calories Burned
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)
span.text-pink-500
i.fas.fa-exchange-alt
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-yellow-600
i.fas.fa-user-plus.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Days Worked
p.font-bold.text-3xl
span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
span.text-yellow-600
i.fas.fa-caret-up
.w-full.p-6
.bg-white.border-transparent.rounded-lg.shadow-xl
.bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
h2.font-bold.uppercase.text-gray-600 Workout Log
.p-5
table.w-full.p-5.text-gray-700
thead
tr
th.text-left.text-blue-900 Date
th.text-left.text-blue-900 Execise
th.text-left.text-blue-900 Amount
th.text-left.text-blue-900 Time
th.text-left.text-blue-900 Calories Burned
tbody
each log in user.workoutLogs.sort((a,b) => a.date - b.date)
tr
td=log.date.toLocaleDateString()
td=log.exercise
td=log.amount + ' ' + log.units
td=log.minutes + ' Minutes'
td=log.calories
As you saw before, this page is protected. Therefore, it requires that you have a user to access this page. The application will pull the user details and workout logs and form them into a summary at the top of the page with a couple of tables of the logs based on the exercises the user has performed.
The last part of this process is to create the actual CSS file that your application will use. In your public/stylesheets
directory, you’ll see a file called style.css
. Replace the code in that file with the code below.
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}
This is largely the same. However, you are importing the directives for each of Tailwind’s layers.
Next, you can compile the classes you used in your pug files and the style sheet you just modified to create a new CSS file. Run the following command.
npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch
Now you can open the output.css
file and see the complete Tailwind CSS code. The --watch
parameter here is helpful for local development. This will rebuild the output.css
file each time you change one of your layouts. You can disregard that parameter if you do not want to make any changes.
You’re now ready to start your application. Run the following command.
npm run start
Once your application is started you should be able to navigate to http://localhost:3000/ and see your home page.
Use the login button at the top of the screen and navigate out to the Okta login page. Once you’ve logged in you will be redirected back to your website where you will be able to see your dashboard page.
In this article, you learned how to use Prisma to define a schema for your database. You then learned how to create a migration from that schema and seed your database. You saw that Prisma could be used with the most popular database options, including SQL Server, MySql, MongoDB, and more.
You also learned how to build a CSS file with Tailwind CSS. We walked through a simple tailwind configuration and how the Tailwind CLI uses your code to build its CSS file.
Finally, you learned how to secure your express application using passport.
Original article sourced at: https://developer.okta.com
1666812900
A fullstack TikTok clone with Nextjs, Prisma, trpc
Official website: https://toptop-clone.vercel.app/
See SELF-HOSTING.md
NEXTAUTH_SECRET=
NEXTAUTH_URL=http://localhost:3000
DATABASE_URL=mysql://username:password@host/database-name
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
FACEBOOK_APP_ID=
FACEBOOK_APP_SECRET=
NEXT_PUBLIC_UPLOAD_URL=https://discloud-storage.herokuapp.com/
NEXT_PUBLIC_IMAGE_UPLOAD_URL=https://discord.com/api/webhooks/id
{
"plugins": ["simple-import-sort"],
"extends": ["next/core-web-vitals"],
"rules": {
"no-unused-vars": "warn",
"@next/next/no-img-element": "off",
"simple-import-sort/imports": "warn",
"simple-import-sort/exports": "warn"
}
}
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
.swc
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
Author: napthedev
Source Code: https://github.com/napthedev/toptop-clone
1663204687
Learn: Full Stack Application with Azure SQL & Prisma in this tutorial
Come along with us to create a scalable, resilient and robust Full Stack application using the main stacks, such as:
✅ Node.Js
✅ Prisma
✅ Azure SQL
✅ Azure Functions
✅ Azure Static Web Apps & GitHub Actions
📕 Course Structure 📕
✅ Module 01 - Prisma Fundamentals
✅ Module 02 - Azure SQL Fundamentals
✅ Module 03 - Azure Static Web Apps Fundamentals
✅ Module 04 - Dev Tools
✅ Module 05 - Application Development
✅ Module 06 - Next Steps
In this course, you'll learn about:
In this video we will see the learning objectives of the Prisma Fundamentals module!
In this video we will learn what is Prisma.
In this video we will learn why we should use Prisma, which has been considered a powerful ORM for JavaScript Developers.
In this video we will learn the main pillars around the Prisma!
Recap on what we learned and how you can dig deeper into Prisma.
In this video you will see what will learn during the Module 02 about Azure SQL.
In this video you will understand what is Azure SQL and the different options like: Single Database and Elastic Pool.
In this video you will understand why is so important to use Azure SQL in your projects!
In this video we will show you that Azure SQL has a perfect fit with Prisma!
Recap on what we learned and how you can dig deeper into Azure SQL.
In this video you will see what will learn during the Module 03 about Azure Static Web Apps.
In this video we will learn what is Azure Static Web Apps.
In this video we will understand under the hood how the Azure Static Web Apps workflow works.
In this video we will understand the benefits to use Azure Static Web Apps!
In this video we will see that can use Azure Static Web Apps with different libs and the most popular frameworks!
Recap on what we learned and how you can dig deeper into Azure Static Web Apps.
In this video we are going to install all the necessary dev tools that we will go into need to develop our application in the BackEnd side.
In this video we will understand the importance around to create a .devContainer folder in any project and a little overview about it!
Recap what we saw during the module 04 and preparing for what is coming!
In this video we will take a first look around the BackEnd application that we are going to build!
In this video we are going to forking/cloning the Front-End starter project!
In this video we are going to structuring the BackEnd project using Azure Functions.
In this video we are going to create our Database Server using the Azure Portal.
Now we already created our Database server, it's time to create our Employee Database using Azure Portal.
In this video we are going to create shadow database using Azure Portal.
In this video we are going to install all the necessary packages for our project!
In this video we are going to learn how to initialize and modeling our database using Prisma.
In this video we are going to understand how Prisma migrations works!
In this video we are going to take a look how Prisma Studio works and use it to manipulate our database.
In this video we're going to create our first function: Create Employee using Prisma & Azure Functions
In this video we are going to develop the functionality to list all the employees using Azure Functions.
In this video we are going to develop the functionality to return an employee by Id using Azure Functions.
In this video we are going to develop the functionality to update an employee by Id using Azure Functions.
In this video we are going to develop the functionality to delete an employee by Id using Azure Functions
In this video we are going to deploy our application using Azure Static Web Apps with GitHub Actions Integration!
In this video we are going to see the next steps that you should see after this course!
⭐️ GitHub repository: https://github.com/glaucia86/azure-sql-prisma-vue
💻 Important Resources & Links Importantes & Links 💻
✅ Build serverless, full stack applications in Azure - Free Course: https://aka.ms/fullstack-mslearn-course
✅ Azure SQL Documentation: https://aka.ms/azuresql-official-documentation
✅ Microsoft Learn [Free Course] - Azure Static Web Apps: https://aka.ms/mslearn-aswa-course
✅ Microsoft Learn [Free Course] - Build Serverless APIs with Azure Functions: https://aka.ms/azurefunctions-msleanr-course
✅ Prisma Official Website: https://www.prisma.io/
✅ Official Prisma Documentation: https://www.prisma.io/docs/
✅ Articles & Updates about Prisma: https://www.prisma.io/blog/
✅ Prisma Community: https://www.prisma.io/community
#azure #azuresql #azurefunctions #prisma #nodejs #javascript #crudapplication #aswa #vuejs, #webdevelopment #tutorial #programming #coding #fullstackapplication
1661401032
0:00 Intro
1:00 Starting point
1:32 Codebase
3:08 Comment model
6:21 API endpoints
15:19 Comment form
22:01 Display comments
30:53 Reply form
34:10 Nest comments funciton
42:00 Display children
48:05 What next?
Repository: https://github.com/TomDoesTech/nested-comments
1661305923
Timestamps
00:00 - Intro
00:15 - What We’ll Build
00:55 - Sponsor Shoutout (RapidAPI)
01:05 - Resources
01:50 - Database Setup in PlanetScale
02:30 - Create a New Next.js Project with TypeScript
03:45 - Setup Prisma with Next.js
05:00 - Configuring Prisma with PlanetScale
06:20 - Database Schema
07:00 - Seed the Database
10:30 - Scaffolding API Next.js Endpoints
11:47 - Testing API Endpoints with the RapidAPI Client VS COde Extension
12:52 - Prisma Client Setup
14:16 - Integrating Prisma Client in Next.js API Endpoint
15:15 - Add Query Parameters and Validation to API Endpoint
18:28 - Adding Config Parameters to Prisma Query
20:25 - Dynamic API Route in Next.js
23:49 - Deploy to Vercel
23:50 - Create New Github Repository
24:44 - Deploy and Host in Vercel
25:53 - Multiple Builds in Vercel
29:10 - Publishing API to RapidAPI
30:22 - Define Endpoints in RapidAPI
32:05 - Monetizing Your API in RapidAPI
34:20 - Wrap Up
RapidAPI Studio - https://rapidapi.com/studio
Rapid API Client VS Code Extension - https://marketplace.visualstudio.com/items?itemName=RapidAPI.vscode-rapidapi-client
Resources
Github Repository - https://github.com/jamesqquick/nextjs-throne-of-glass-api
#serverless #api #nextjs #vercel #prisma #rapidapi
1659933162
Learn how to build a full stack nested comments system - This tutorial shows how to create nested comment system with React and Prisma. Learn how to setup and connect a backend, database, and React frontend.
If you are wanting to build a complex full stack project then this is the video for you. In this video we will be covering how to setup and connect a backend, database, and React frontend. This is a large and complex project, but is amazing for your resume.
⏱️ Timestamps:
00:00:00 - Introduction
00:01:55 - Prisma database setup
00:13:26 - Server setup
00:21:01 - Connecting the client to the server
00:30:57 - React Router setup
00:33:19 - useAsync custom hook
00:41:58 - Get post server route
00:44:23 - Post context setup
00:50:44 - Render comments
01:09:30 - Comment form component
01:15:45 - Create comment client logic
01:19:15 - Create comment server logic
01:21:21 - User authentication
01:26:00 - Saving comments on the client
01:30:15 - Comment reply logic
01:34:35 - Update comment server logic
01:38:00 - Update comment client logic
01:42:53 - Delete comment server logic
01:43:54 - Delete comment client logic
01:46:23 - User permission UI
01:48:38 - Like comments server logic
01:57:55 - Like comments client logic
GitHub Code: https://github.com/WebDevSimplified/nested-comments
Subscribe: https://www.youtube.com/c/WebDevSimplified/featured
#react #prisma
1659023820
Prisma est un outil ORM (mappage objet-relationnel) pour Node.js utilisant TypeScript. Le logiciel s'intègre à la plupart des bases de données les plus populaires aujourd'hui, notamment MySQL, SQL Server, SQLite et MongoDB, et met l'accent sur un schéma lisible par l'homme avec un client de base de données de type sécurisé. Prisma inclut également d'autres fonctionnalités telles que les migrations, les données de départ et un navigateur de base de données virtuelle.
Dans ce projet, vous utiliserez Prisma pour connecter votre application Express à un serveur de base de données. Vous allez créer un schéma pour modéliser une application de suivi d'entraînement. Ensuite, vous créerez des données de départ et utiliserez Prisma pour exécuter des migrations et amorcer votre base de données. Enfin, vous créerez l'application Web à l'aide de Pug et Tailwind CSS pour créer l'interface de l'application.
Conditions préalables
Ce tutoriel utilise les technologies suivantes mais ne nécessite aucune expérience préalable :
Si vous souhaitez ignorer le didacticiel et consulter le projet entièrement construit, vous pouvez le consulter sur GitHub .
Créez un nouveau répertoire pour votre application. Utilisez la cd
commande pour accéder à ce dossier.
Avant de commencer, vous aurez besoin d'un compte développeur Okta gratuit. Installez l' interface de ligne de commande Okta et exécutez- okta register
la pour vous inscrire à un nouveau compte. Si vous avez déjà un compte, exécutez okta login
. Ensuite, courez okta apps create
. Sélectionnez le nom de l'application par défaut ou modifiez-le comme bon vous semble. Choisissez Web et appuyez sur Entrée .
Sélectionnez Autre . Ensuite, remplacez l'URI de redirection par http://localhost:3000/authorization-code/callback
et utilisez http://localhost:3000/
-le pour l'URI de redirection de déconnexion.
Que fait la CLI d'Okta ?
L'Okta CLI créera une application Web OIDC dans votre Okta Org. Il ajoutera les URI de redirection que vous avez spécifiés et accordera l'accès au groupe Tout le monde. Vous verrez une sortie comme celle-ci lorsqu'elle sera terminée :
Okta application configuration has been written to: /path/to/app/.okta.env
Exécutez cat .okta.env
(ou type .okta.env
sous Windows) pour voir l'émetteur et les informations d'identification de votre application.
export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"
Votre domaine Okta est la première partie de votre émetteur, avant /oauth2/default
.
REMARQUE : Vous pouvez également utiliser la console d'administration Okta pour créer votre application. Voir Créer une application Web pour plus d'informations.
Ensuite, vous utiliserez l' express-generator
outil générateur d'applications pour échafauder rapidement votre application. Exécutez la commande suivante.
npx express-generator@4.16 --view=pug
Vous pouvez maintenant installer vos packages.
npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
@prisma/client
est utilisé pour accéder à la base de données à partir de votre code serveur.dotenv
lit les paramètres de configuration à partir de .env
fichiers tels que celui produit par la CLI Okta.passport
est un middleware pour Node.js suffisamment flexible pour gérer la plupart des scénarios d'authentification, y compris Okta. passport-openidconnect
est un module pour passeport qui vous permet de vous authentifier avec OpenID Connect.express-session
est nécessaire pour le passeport. Les applications Express utilisent ce package pour la prise en charge des sessions. Votre application doit initialiser le support de session pour utiliser le passeport.Tailwind CSS
est le framework CSS que vous utiliserez. Si vous n'avez jamais utilisé Tailwind auparavant, vous vous demandez peut-être pourquoi il s'agit d'une dépendance au développement. En effet, Tailwind créera dynamiquement vos fichiers CSS à partir de vos vues et de votre configuration. Plus sur cela plus tard.prisma
est l'ORM que vous utilisez. Il s'agit d'une dépendance de développement car la bibliothèque Prisma gère les migrations, les données de départ, etc., tandis que le @prisma/client
package est utilisé dans votre application au moment de l'exécution.À ce stade, vous allez initialiser Tailwind CSS et Prisma. Vous les configurerez pour une utilisation ultérieure.
Commencez avec Prisma et exécutez la commande suivante.
npx prisma init
Cette commande ajoutera un nouveau dossier nommé prisma
à votre application. Il ajoute également un fichier appelé .env
à votre projet avec une configuration par défaut. Remplacez le contenu de ce fichier par le code ci-dessous.
# 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="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}
Dans cet exemple, j'ai utilisé SQLite parce que c'est simple et compact. Mais l'un des avantages de Prisma et de la plupart des ORM est qu'ils peuvent prendre en charge de nombreuses bases de données. Le code passe-partout inclut des commentaires sur la configuration de votre application pour d'autres serveurs de base de données. N'hésitez pas à utiliser ce avec quoi vous vous sentez le plus à l'aise et utilisez la chaîne de connexion appropriée.
Vos données de départ utiliseront le SEED_USER_NAME
paramètre. Vous voulez vous assurer qu'il s'agit du même nom d'utilisateur Okta avec lequel vous vous connectez. Cela permettra à l'application d'associer votre utilisateur connecté aux données que vous ajouterez à votre base de données.
Ensuite, mettez à jour votre package.json
fichier avec le code suivant.
{
"name": "workout-app",
"version": "0.0.0",
"private": true,
"prisma": {
"seed": "node prisma/seed.js"
},
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"@prisma/client": "^3.13.0",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^16.0.0",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"passport": "^0.5.2",
"passport-openidconnect": "^0.1.1",
"pug": "2.0.0-beta11"
},
"devDependencies": {
"prisma": "^3.13.0",
"tailwindcss": "^3.0.24"
}
}
Cela ajoutera la prisma seed
commande dont vous avez besoin plus tard.
Vous pouvez maintenant initialiser Tailwind CSS.
npx tailwindcss init
Vous avez maintenant ajouté un nouveau fichier appelé tailwind.config.js
à la racine de votre application. Remplacez le contenu de ce fichier par le code ci-dessous.
module.exports = {
content: ["./views/**/*.pug"],
theme: {
extend: {},
},
plugins: [],
}
Dans cette étape, vous indiquez à Tailwind CSS où trouver les classes que vous avez utilisées dans votre application. Vous souhaitez que Tailwind recherche dans les .pug
fichiers de votre views
répertoire. theme
Tailwind CSS est hautement extensible, comme le montrent les paramètres et les objets de plugins
configuration. Une plongée approfondie dans ce sujet n'entre pas dans le cadre de cet article, mais je vous encourage à consulter le site de Tailwind CSS pour plus d'informations.
La tâche suivante consiste à créer la base de données pour votre application. Les étapes impliqueront l'écriture du schéma, l'écriture de données de départ, la création de la migration et l'application de la migration, qui amorcera également vos données.
La prisma init
tâche ci-dessus devrait avoir ajouté un fichier appelé schema.prisma
dans votre prisma
répertoire. Remplacez le code par le code ci-dessous.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "{yourDatabaseProvider}"
url = env("DATABASE_URL")
}
model WorkoutLog {
id Int @id @default(autoincrement())
userId Int
exercise String
amount Decimal
units String
date DateTime
minutes Int
calories Int
user User @relation(fields: [userId], references: [id])
}
model User {
id Int @id @default(autoincrement())
username String @unique
workoutLogs WorkoutLog[]
}
Assurez-vous de remplacer {yourDatabaseProvider}
par le fournisseur que vous utilisez . Comme vous pouvez le voir, ce fichier définit les objets User
et WorkoutLog
que vous stockerez dans la base de données. Le WorkoutLog
contient des informations de base sur l'entraînement de l'utilisateur un jour spécifique, puis associe cet enregistrement à l' User
objet. Vous pouvez également définir des clés, des contraintes et des index à partir de ce fichier. Notez que la user
propriété sur WorkoutLog
a une relation définie par le userId
sur le id
de la User
table.
Ajoutez un fichier au prisma
répertoire nommé seed.js
et ajoutez-y le code suivant.
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
require("dotenv").config();
async function main() {
const user = await prisma.user.upsert({
where: { username: process.env.SEED_USER_NAME },
update: {},
create: {
username: process.env.SEED_USER_NAME,
workoutLogs: {
create: [
{
exercise: "Running",
amount: 1,
units: "Miles",
date: new Date(2022, 1, 1),
minutes: 8,
calories: 100
},
{
exercise: "Running",
amount: 1.2,
units: "Miles",
date: new Date(2022, 1, 3),
minutes: 10,
calories: 120
},
{
exercise: "Running",
amount: 1.5,
units: "Miles",
date: new Date(2022, 1, 5),
minutes: 12,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 1),
minutes: 15,
calories: 100
},
{
exercise: "Heavy Bag",
amount: 6,
units: "Rounds",
date: new Date(2022, 1, 3),
minutes: 22,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 5),
minutes: 15,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 10,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 10,
calories: 100
},
],
},
},
});
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Prisma sait utiliser le seed.js
fichier à partir de la commande que vous avez ajoutée à package.json
. Ce fichier insérera les données lorsque vous exécuterez cette commande.
Vous pouvez maintenant ajouter et appliquer la migration à l'aide de la CLI Prisma. À partir de la racine du répertoire de votre application, exécutez la commande suivante.
npx prisma migrate dev --name init
L'interface de ligne de commande propose plusieurs façons d'ajouter et d'appliquer des migrations. Je vous suggère de comprendre la meilleure façon de gérer les migrations pour votre environnement. La méthode ci-dessus est le moyen le plus simple et le plus rapide de préparer votre base de données. Dans le cadre du processus de migration, cette commande recherchera la seed
commande de votre package.json
et l'exécutera également. Une fois terminée, votre base de données devrait être prête avec une base de données pleine de données de départ avec lesquelles travailler.
Maintenant que votre base de données est prête, vous pouvez vous concentrer sur le cœur de votre application.
Tout d'abord, ajoutez un nouveau fichier à la racine de votre application appelée ensureLoggedIn.js
et ajoutez-y le code suivant.
function ensureLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login')
}
module.exports = ensureLoggedIn;
Ce petit middleware s'assurera que l'utilisateur est authentifié. S'ils ne le sont pas, vous les redirigerez vers la login
route, que vous configurerez pour utiliser Okta. Sinon, vous permettrez à l'utilisateur d'accéder à l'écran suivant.
Ensuite, vous pouvez mettre à jour vos routes dans l' routes
annuaire. Tout d'abord, supprimez- users.js
le car vous ne l'utiliserez pas. Remplacez le code dans index.js
par ce qui suit.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});
module.exports = router;
Ici, vous passerez une propriété pour isAuthenticated
que votre page de mise en page puisse afficher correctement un bouton de connexion ou de déconnexion.
Ensuite, ajoutez un fichier dans le routes
répertoire pour dashboard.js
avec le code suivant.
var express = require("express");
var router = express.Router();
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
const ensureLoggedIn = require("../ensureLoggedIn");
/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
const username = req.user.username;
const dbUser = await prisma.user.findUnique({
where: {
username: username,
},
include: {
workoutLogs: true,
},
});
res.render("dashboard", {
title: "The Workout Tracker - Dashboard",
isAuthenticated: req.isAuthenticated(),
user: dbUser
});
});
module.exports = router;
Il y a beaucoup de magie dans ce parcours. Tout d'abord, vous utilisez le ensureLoggedIn
middleware pour protéger la route. Ensuite, vous pouvez extraire le nom d'utilisateur de la demande et rechercher l'utilisateur dans la base de données à l'aide du client Prisma. Étant donné que l'utilisateur a une propriété pour ses journaux d'entraînement, vous pouvez passer l'utilisateur en tant que modèle et analyser les journaux sur la vue carlin que vous allez créer. Cela donnera également un accès en vue au nom de l'utilisateur. Bien sûr, vous pouvez interroger workoutLogs
directement la table et inclure des fonctionnalités telles que la pagination et la recherche si cette approche correspond mieux à votre flux de travail.
Maintenant, remplacez le code app.js
par ce qui suit.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');
const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');
var app = express();
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient();
require('dotenv').config({path:'./.okta.env'});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(session({
secret: 'CanYouLookTheOtherWay',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;
// set up passport
passport.use('oidc', new Strategy({
issuer: OKTA_OAUTH2_ISSUER,
authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
clientID: OKTA_OAUTH2_CLIENT_ID,
clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/authorization-code/callback',
scope: 'openid profile'
}, (issuer, profile, done) => {
return done(null, profile);
}));
passport.serializeUser((user, next) => {
next(null, user);
});
passport.deserializeUser((obj, next) => {
next(null, obj);
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/login', passport.authenticate('oidc'));
app.use('/authorization-code/callback',
passport.authenticate('oidc', { failureRedirect: '/error' }),
(req, res, next) => {
res.redirect('/dashboard');
}
);
app.post('/logout', (req, res) => {
req.logout();
req.session.destroy();
res.redirect('/');
});
app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Vous avez mis à jour vos itinéraires pour utiliser correctement les dashboard
itinéraires ainsi que tous les itinéraires de connexion ou de déconnexion qu'Okta utilisera. Vous configurez également un passeport pour utiliser Okta en utilisant les valeurs produites par la CLI d'Okta dans le .okta.env
fichier.
Par défaut, express-generator
ajoutera quelques vues pour vous. Vous devrez les modifier et en ajouter un nouveau de votre cru. Commencez par ouvrir le layout.pug
fichier dans le views
répertoire et remplacez le code par ce qui suit.
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/output.css')
body
nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
.w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
.text-sm(class='lg:flex-grow')
div
if(isAuthenticated)
form(action="logout" method="POST")
button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
else
a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
block content
Vous remarquerez plusieurs choses à propos de ce fichier. Tout d'abord, les cours sont très spécifiques et nombreux. Ceci est typique du CSS Tailwind. Plutôt que de définir l'apparence d'un élément spécifique, Tailwind CSS s'efforce de définir l'apparence d'un style particulier, puis vous pouvez ajouter et supprimer des styles d'éléments pour s'adapter à l'apparence souhaitée.
L'autre chose que vous verrez est le bouton Connexion/Déconnexion qui changera en fonction de la isAuthenticated
propriété.
Ensuite, vous pouvez mettre à jour index.pug
avec ce qui suit.
extends layout
block content
.py-12.bg-white
.max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
div.c(class='lg:text-center')
p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
| A small demo built with
a(href="https://expressjs.com/") Express on
a(href="https://nodejs.org/en/") Node.JS
| Secured with
a(href="https://developer.okta.com/signup") Okta.
br
| This app uses
a(href="https://tailwindcss.com/") Tailwind CSS
| for its CSS framework and
a(href="https://www.prisma.io/") Prisma.JS
| for its ORM.
Il n'y a pas grand chose à dire ici. Juste une page d'accueil avec un peu plus de CSS Tailwind et quelques liens vers les technologies utilisées.
Enfin, ajoutez un fichier pour dashboard.pug
avec le code suivant.
extends layout
block content
.flex.flex-wrap
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-green-600
i.fa.fa-wallet.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0)
span.text-green-500
i.fas.fa-caret-up
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-pink-600
i.fas.fa-users.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Calories Burned
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)
span.text-pink-500
i.fas.fa-exchange-alt
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-yellow-600
i.fas.fa-user-plus.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Days Worked
p.font-bold.text-3xl
span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
span.text-yellow-600
i.fas.fa-caret-up
.w-full.p-6
.bg-white.border-transparent.rounded-lg.shadow-xl
.bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
h2.font-bold.uppercase.text-gray-600 Workout Log
.p-5
table.w-full.p-5.text-gray-700
thead
tr
th.text-left.text-blue-900 Date
th.text-left.text-blue-900 Execise
th.text-left.text-blue-900 Amount
th.text-left.text-blue-900 Time
th.text-left.text-blue-900 Calories Burned
tbody
each log in user.workoutLogs.sort((a,b) => a.date - b.date)
tr
td=log.date.toLocaleDateString()
td=log.exercise
td=log.amount + ' ' + log.units
td=log.minutes + ' Minutes'
td=log.calories
Comme vous l'avez vu précédemment, cette page est protégée. Par conséquent, il faut que vous ayez un utilisateur pour accéder à cette page. L'application extrait les détails de l'utilisateur et les journaux d'entraînement et les forme dans un résumé en haut de la page avec quelques tableaux des journaux basés sur les exercices que l'utilisateur a effectués.
La dernière partie de ce processus consiste à créer le fichier CSS réel que votre application utilisera. Dans votre public/stylesheets
répertoire, vous verrez un fichier appelé style.css
. Remplacez le code de ce fichier par le code ci-dessous.
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}
C'est en grande partie la même chose. Cependant, vous importez les directives pour chacune des couches de Tailwind.
Ensuite, vous pouvez compiler les classes que vous avez utilisées dans vos fichiers pug et la feuille de style que vous venez de modifier pour créer un nouveau fichier CSS. Exécutez la commande suivante.
npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch
Vous pouvez maintenant ouvrir le output.css
fichier et voir le code CSS Tailwind complet. Le --watch
paramètre ici est utile pour le développement local. Cela reconstruira le output.css
fichier chaque fois que vous modifierez l'une de vos mises en page. Vous pouvez ignorer ce paramètre si vous ne souhaitez apporter aucune modification.
Vous êtes maintenant prêt à démarrer votre application. Exécutez la commande suivante.
npm run start
Une fois votre application démarrée, vous devriez pouvoir accéder à http://localhost:3000/ et voir votre page d'accueil.
Utilisez le bouton de connexion en haut de l'écran et accédez à la page de connexion d'Okta. Une fois connecté, vous serez redirigé vers votre site Web où vous pourrez voir votre page de tableau de bord.
Dans cet article, vous avez appris à utiliser Prisma pour définir un schéma pour votre base de données. Vous avez ensuite appris à créer une migration à partir de ce schéma et à amorcer votre base de données. Vous avez vu que Prisma pouvait être utilisé avec les options de base de données les plus populaires, notamment SQL Server, MySql, MongoDB, etc.
Vous avez également appris à créer un fichier CSS avec Tailwind CSS. Nous avons parcouru une configuration simple de Tailwind et comment la CLI Tailwind utilise votre code pour créer son fichier CSS.
Enfin, vous avez appris comment sécuriser votre demande express à l'aide d'un passeport.
Lien : https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind
#tailwindcss #express #prisma
1659012960
Prisma — это инструмент ORM (объектно-реляционное сопоставление) для Node.js с использованием TypeScript. Программное обеспечение интегрируется со многими из самых популярных на сегодняшний день баз данных, включая MySQL, SQL Server, SQLite и MongoDB, и делает упор на удобочитаемую схему с безопасным типом клиента базы данных. Prisma также включает в себя другие функции, такие как миграция, начальные данные и виртуальный браузер базы данных.
В этом проекте вы будете использовать Prisma для подключения вашего приложения Express к серверу базы данных. Вы создадите схему для моделирования приложения для отслеживания тренировок. Затем вы создадите некоторые исходные данные и используете Prisma для запуска миграции и заполнения базы данных. Наконец, вы создадите веб-приложение, используя Pug и Tailwind CSS для построения внешнего интерфейса приложения.
Предпосылки
В этом руководстве используются следующие технологии, но не требуется никакого предварительного опыта:
Если вы хотите пропустить руководство и ознакомиться с полностью собранным проектом, вы можете посмотреть его на GitHub .
Создайте новый каталог для вашего приложения. Используйте cd
команду для перехода к этой папке.
Прежде чем начать, вам понадобится бесплатная учетная запись разработчика Okta. Установите Okta CLI и запустите okta register
регистрацию новой учетной записи. Если у вас уже есть учетная запись, запустите okta login
. Затем бегите okta apps create
. Выберите имя приложения по умолчанию или измените его по своему усмотрению. Выберите Интернет и нажмите Enter .
Выберите Другое . Затем измените URI перенаправления на http://localhost:3000/authorization-code/callback
и используйте http://localhost:3000/
для URI перенаправления выхода из системы.
Что делает интерфейс командной строки Okta?
Интерфейс командной строки Okta создаст веб-приложение OIDC в вашей организации Okta. Он добавит указанные вами URI перенаправления и предоставит доступ группе «Все». Вы увидите вывод, подобный следующему, когда он будет завершен:
Okta application configuration has been written to: /path/to/app/.okta.env
Запустите cat .okta.env
(или type .okta.env
в Windows), чтобы увидеть издателя и учетные данные для вашего приложения.
export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"
Ваш домен Okta — это первая часть вашего эмитента, до /oauth2/default
.
ПРИМЕЧАНИЕ . Вы также можете использовать Okta Admin Console для создания своего приложения. Дополнительные сведения см. в разделе Создание веб-приложения .
Далее вы будете использовать express-generator
инструмент генератора приложений, чтобы быстро сформировать шаблон вашего приложения. Выполните следующую команду.
npx express-generator@4.16 --view=pug
Теперь вы можете установить свои пакеты.
npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
@prisma/client
используется для доступа к базе данных из кода вашего сервера.dotenv
считывает параметры конфигурации из .env
файлов, подобных тому, который создается интерфейсом командной строки Okta.passport
— это промежуточное ПО для Node.js, достаточно гибкое для обработки большинства сценариев аутентификации, включая Okta. passport-openidconnect
это модуль для паспорта, который позволяет вам аутентифицироваться с помощью OpenID Connect.express-session
требуется для загранпаспорта. Экспресс-приложения используют этот пакет для поддержки сеансов. Ваше приложение должно инициализировать поддержку сеанса для использования паспорта.Tailwind CSS
это CSS-фреймворк, который вы будете использовать. Если вы никогда раньше не использовали Tailwind, вам может быть интересно, почему это зависит от разработки. Это связано с тем, что Tailwind будет динамически создавать файлы CSS из ваших представлений и конфигурации. Подробнее об этом позже.prisma
это ORM, который вы используете. Это зависимость от разработчиков, потому что библиотека Prisma обрабатывает миграции, начальные данные и т. д., в то время как @prisma/client
пакет используется в вашем приложении во время выполнения.На этом этапе вы инициализируете Tailwind CSS и Prisma. Вы настроите их для использования позже.
Начните с Prisma и выполните следующую команду.
npx prisma init
Эта команда добавит новую папку с именем prisma
в ваше приложение. Он также добавляет .env
в ваш проект файл с некоторой конфигурацией по умолчанию. Замените содержимое этого файла приведенным ниже кодом.
# 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="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}
В этом примере я использовал SQLite, потому что он простой и компактный. Но одна из замечательных особенностей Prisma и большинства ORM заключается в том, что они могут поддерживать множество баз данных. Стандартный код включает комментарии о настройке вашего приложения для других серверов баз данных. Не стесняйтесь использовать то, что вам удобнее, и используйте соответствующую строку подключения.
Ваши исходные данные будут использовать эту SEED_USER_NAME
настройку. Вы хотите убедиться, что оно совпадает с именем пользователя Okta, с которым вы входите в систему. Это позволит приложению связать вошедшего в систему пользователя с данными, которые вы введете в свою базу данных.
Затем обновите package.json
файл с помощью следующего кода.
{
"name": "workout-app",
"version": "0.0.0",
"private": true,
"prisma": {
"seed": "node prisma/seed.js"
},
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"@prisma/client": "^3.13.0",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^16.0.0",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"passport": "^0.5.2",
"passport-openidconnect": "^0.1.1",
"pug": "2.0.0-beta11"
},
"devDependencies": {
"prisma": "^3.13.0",
"tailwindcss": "^3.0.24"
}
}
Это добавит prisma seed
команду, которая вам понадобится позже.
Теперь вы можете инициализировать Tailwind CSS.
npx tailwindcss init
Теперь вы добавили новый файл с именем tailwind.config.js
в корень вашего приложения. Замените содержимое этого файла приведенным ниже кодом.
module.exports = {
content: ["./views/**/*.pug"],
theme: {
extend: {},
},
plugins: [],
}
На этом шаге вы сообщаете Tailwind CSS, где найти классы, которые вы использовали в своем приложении. Вы хотите, чтобы Tailwind искал .pug
файлы в вашем views
каталоге. Tailwind CSS обладает широкими возможностями расширения, что видно по объектам конфигурации theme
и plugins
настройкам. Подробное рассмотрение этого вопроса выходит за рамки данной статьи, но я рекомендую вам заглянуть на сайт Tailwind CSS для получения дополнительной информации.
Следующей задачей является создание базы данных для вашего приложения. Шаги будут включать в себя написание схемы, запись некоторых начальных данных, создание миграции и применение миграции, которая также заполнит ваши данные.
Задача prisma init
сверху должна была добавить файл с именем schema.prisma
в ваш prisma
каталог. Замените код там кодом ниже.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "{yourDatabaseProvider}"
url = env("DATABASE_URL")
}
model WorkoutLog {
id Int @id @default(autoincrement())
userId Int
exercise String
amount Decimal
units String
date DateTime
minutes Int
calories Int
user User @relation(fields: [userId], references: [id])
}
model User {
id Int @id @default(autoincrement())
username String @unique
workoutLogs WorkoutLog[]
}
Убедитесь, что вы заменили {yourDatabaseProvider}
провайдера , которого используете . Как видите, этот файл определяет объекты User
и WorkoutLog
, которые вы будете хранить в базе данных. Он WorkoutLog
содержит основную информацию о тренировке пользователя в определенный день, а затем связывает эту запись с User
объектом. Вы также можете определить ключи, ограничения и индексы из этого файла. Обратите внимание, что user
свойство WorkoutLog
имеет отношение, определенное userId
к id
таблице User
.
Добавьте файл в указанный prisma
каталог seed.js
и добавьте в него следующий код.
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
require("dotenv").config();
async function main() {
const user = await prisma.user.upsert({
where: { username: process.env.SEED_USER_NAME },
update: {},
create: {
username: process.env.SEED_USER_NAME,
workoutLogs: {
create: [
{
exercise: "Running",
amount: 1,
units: "Miles",
date: new Date(2022, 1, 1),
minutes: 8,
calories: 100
},
{
exercise: "Running",
amount: 1.2,
units: "Miles",
date: new Date(2022, 1, 3),
minutes: 10,
calories: 120
},
{
exercise: "Running",
amount: 1.5,
units: "Miles",
date: new Date(2022, 1, 5),
minutes: 12,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 1),
minutes: 15,
calories: 100
},
{
exercise: "Heavy Bag",
amount: 6,
units: "Rounds",
date: new Date(2022, 1, 3),
minutes: 22,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 5),
minutes: 15,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 10,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 10,
calories: 100
},
],
},
},
});
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Prisma знает, что нужно использовать seed.js
файл из команды, которую вы добавили в package.json
. Этот файл будет вставлять данные при запуске этой команды.
Теперь вы можете добавить и применить миграцию с помощью интерфейса командной строки Prisma. В корневом каталоге вашего приложения выполните следующую команду.
npx prisma migrate dev --name init
В интерфейсе командной строки есть несколько способов добавления и применения миграций. Я предлагаю вам понять, как лучше всего управлять миграциями для вашей среды. Описанный выше метод — самый простой и быстрый способ подготовить базу данных. В рамках процесса миграции эта команда будет искать seed
команду у вас package.json
и запускать ее. После завершения ваша база данных должна быть готова с базой данных, полной исходных данных для работы.
Теперь, когда ваша база данных готова, вы можете обратить внимание на ядро вашего приложения.
Во-первых, добавьте новый файл в корень вашего приложения с именем ensureLoggedIn.js
и добавьте в него следующий код.
function ensureLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login')
}
module.exports = ensureLoggedIn;
Этот небольшой компонент промежуточного программного обеспечения обеспечит аутентификацию пользователя. Если это не так, вы перенаправите их на login
маршрут, который вы настроите для использования Okta. В противном случае вы позволите пользователю перейти на следующий экран.
Далее вы можете обновить свои маршруты в routes
каталоге. Во-первых, удалите, users.js
поскольку вы не будете использовать это. Замените код index.js
следующим.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});
module.exports = router;
Здесь вы передадите свойство isAuthenticated
, чтобы ваша страница макета могла правильно отображать кнопку входа или выхода.
Затем добавьте файл в routes
каталог для dashboard.js
со следующим кодом.
var express = require("express");
var router = express.Router();
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
const ensureLoggedIn = require("../ensureLoggedIn");
/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
const username = req.user.username;
const dbUser = await prisma.user.findUnique({
where: {
username: username,
},
include: {
workoutLogs: true,
},
});
res.render("dashboard", {
title: "The Workout Tracker - Dashboard",
isAuthenticated: req.isAuthenticated(),
user: dbUser
});
});
module.exports = router;
В этом маршруте много волшебства. Во-первых, вы используете ensureLoggedIn
промежуточное ПО для защиты маршрута. Затем вы можете извлечь имя пользователя из запроса и найти пользователя в базе данных с помощью клиента Prisma. Поскольку у пользователя есть свойство для его журналов тренировок, вы можете передать пользователя в качестве модели и проанализировать журналы в представлении мопса, которое вы создадите. Это также даст доступ к просмотру имени пользователя. Конечно, вы можете запросить workoutLogs
таблицу напрямую и включить такие функции, как разбиение на страницы и поиск, если этот подход лучше подходит для вашего рабочего процесса.
Теперь замените код app.js
на следующий.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');
const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');
var app = express();
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient();
require('dotenv').config({path:'./.okta.env'});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(session({
secret: 'CanYouLookTheOtherWay',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;
// set up passport
passport.use('oidc', new Strategy({
issuer: OKTA_OAUTH2_ISSUER,
authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
clientID: OKTA_OAUTH2_CLIENT_ID,
clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/authorization-code/callback',
scope: 'openid profile'
}, (issuer, profile, done) => {
return done(null, profile);
}));
passport.serializeUser((user, next) => {
next(null, user);
});
passport.deserializeUser((obj, next) => {
next(null, obj);
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/login', passport.authenticate('oidc'));
app.use('/authorization-code/callback',
passport.authenticate('oidc', { failureRedirect: '/error' }),
(req, res, next) => {
res.redirect('/dashboard');
}
);
app.post('/logout', (req, res) => {
req.logout();
req.session.destroy();
res.redirect('/');
});
app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Вы обновили свои маршруты, чтобы правильно использовать dashboard
маршруты, а также любые маршруты входа или выхода, которые будет использовать Okta. Вы также настроили паспорт для использования Okta, используя значения, созданные интерфейсом командной строки Okta в .okta.env
файле.
По умолчанию express-generator
добавит вам несколько просмотров. Вам нужно будет отредактировать их и добавить новый собственный. Начните с открытия layout.pug
файла в views
каталоге и замены кода следующим.
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/output.css')
body
nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
.w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
.text-sm(class='lg:flex-grow')
div
if(isAuthenticated)
form(action="logout" method="POST")
button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
else
a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
block content
Вы заметите пару вещей об этом файле. Во-первых, занятия очень специфичны и многочисленны. Это типично для Tailwind CSS. Вместо того, чтобы определять, как выглядит конкретный элемент, Tailwind CSS стремится определить, как выглядит конкретный стиль, после чего вы можете добавлять и удалять стили из элементов, чтобы они соответствовали желаемому виду.
Еще одна вещь, которую вы увидите, — это кнопка входа/ выхода , которая будет меняться в зависимости от isAuthenticated
свойства.
Затем вы можете обновить index.pug
следующее.
extends layout
block content
.py-12.bg-white
.max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
div.c(class='lg:text-center')
p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
| A small demo built with
a(href="https://expressjs.com/") Express on
a(href="https://nodejs.org/en/") Node.JS
| Secured with
a(href="https://developer.okta.com/signup") Okta.
br
| This app uses
a(href="https://tailwindcss.com/") Tailwind CSS
| for its CSS framework and
a(href="https://www.prisma.io/") Prisma.JS
| for its ORM.
Здесь не о чем говорить. Просто заставка с дополнительным CSS-кодом Tailwind и некоторыми ссылками на используемые технологии.
Наконец, добавьте файл для dashboard.pug
со следующим кодом.
extends layout
block content
.flex.flex-wrap
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-green-600
i.fa.fa-wallet.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0)
span.text-green-500
i.fas.fa-caret-up
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-pink-600
i.fas.fa-users.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Calories Burned
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)
span.text-pink-500
i.fas.fa-exchange-alt
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-yellow-600
i.fas.fa-user-plus.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Days Worked
p.font-bold.text-3xl
span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
span.text-yellow-600
i.fas.fa-caret-up
.w-full.p-6
.bg-white.border-transparent.rounded-lg.shadow-xl
.bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
h2.font-bold.uppercase.text-gray-600 Workout Log
.p-5
table.w-full.p-5.text-gray-700
thead
tr
th.text-left.text-blue-900 Date
th.text-left.text-blue-900 Execise
th.text-left.text-blue-900 Amount
th.text-left.text-blue-900 Time
th.text-left.text-blue-900 Calories Burned
tbody
each log in user.workoutLogs.sort((a,b) => a.date - b.date)
tr
td=log.date.toLocaleDateString()
td=log.exercise
td=log.amount + ' ' + log.units
td=log.minutes + ' Minutes'
td=log.calories
Как вы видели ранее, эта страница защищена. Поэтому для доступа к этой странице требуется, чтобы у вас был пользователь. Приложение извлечет данные о пользователе и журналы тренировок и сформирует их в сводку в верхней части страницы с парой таблиц журналов на основе упражнений, которые выполнил пользователь.
Последней частью этого процесса является создание фактического файла CSS, который будет использовать ваше приложение. В вашем public/stylesheets
каталоге вы увидите файл с именем style.css
. Замените код в этом файле кодом ниже.
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}
Это во многом то же самое. Однако вы импортируете директивы для каждого из слоев Tailwind.
Затем вы можете скомпилировать классы, которые вы использовали в своих файлах pug, и таблицу стилей, которую вы только что изменили, чтобы создать новый файл CSS. Выполните следующую команду.
npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch
Теперь вы можете открыть output.css
файл и увидеть полный CSS-код Tailwind. Параметр --watch
здесь полезен для местного развития. Это будет перестраивать output.css
файл каждый раз, когда вы меняете один из ваших макетов. Вы можете игнорировать этот параметр, если не хотите вносить какие-либо изменения.
Теперь вы готовы запустить свое приложение. Выполните следующую команду.
npm run start
После запуска приложения вы сможете перейти по адресу http://localhost:3000/ и увидеть свою домашнюю страницу.
Используйте кнопку входа в верхней части экрана и перейдите на страницу входа в Okta. После того, как вы вошли в систему, вы будете перенаправлены обратно на свой веб-сайт, где вы сможете увидеть свою страницу панели инструментов.
В этой статье вы узнали, как использовать Prisma для определения схемы вашей базы данных. Затем вы узнали, как создать миграцию из этой схемы и заполнить базу данных. Вы видели, что Prisma можно использовать с самыми популярными вариантами баз данных, включая SQL Server, MySql, MongoDB и другие.
Вы также узнали, как создать файл CSS с помощью Tailwind CSS. Мы рассмотрели простую конфигурацию попутного ветра и то, как интерфейс командной строки Tailwind использует ваш код для создания файла CSS.
Наконец, вы узнали, как защитить экспресс-заявку с помощью паспорта.
Ссылка: https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind
1659012496
Prisma is an ORM (object–relational mapping) tool for Node.js using TypeScript. The software integrates with many of the most popular databases today, including MySQL, SQL Server, SQLite, and MongoDB, and emphasizes a human-readable schema with a type-safe database client. Prisma also includes other features such as migrations, seed data, and a virtual database browser.
In this project, you will use Prisma to connect your Express application to a database server. You will build a schema to model a workout tracker application. Then you’ll create some seed data and use Prisma to run migrations and seed your database. Finally, you’ll create the web application using Pug and Tailwind CSS to build the application frontend.
See more at: https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind
1659002100
Prisma 是一個使用 TypeScript 的 Node.js 的 ORM(對象-關係映射)工具。該軟件與當今許多最流行的數據庫集成,包括 MySQL、SQL Server、SQLite 和 MongoDB,並強調具有類型安全數據庫客戶端的人類可讀模式。Prisma 還包括其他功能,例如遷移、種子數據和虛擬數據庫瀏覽器。
在這個項目中,您將使用 Prisma 將您的Express應用程序連接到數據庫服務器。您將構建一個模式來為鍛煉跟踪器應用程序建模。然後,您將創建一些種子數據並使用 Prisma 運行遷移並為您的數據庫提供種子。最後,您將使用Pug和Tailwind CSS創建 Web 應用程序來構建應用程序前端。
先決條件
本教程使用以下技術,但不需要任何先前的經驗:
如果您想跳過本教程並查看完整構建的項目,可以在 GitHub 上查看。
為您的應用程序創建一個新目錄。使用該cd
命令導航到該文件夾。
在開始之前,您需要一個免費的 Okta 開發者帳戶。安裝Okta CLI並運行okta register
以註冊一個新帳戶。如果您已經有一個帳戶,請運行okta login
. 然後,運行okta apps create
。選擇默認應用名稱,或根據需要進行更改。選擇Web並按Enter。
選擇其他。然後,將重定向 URI 更改為http://localhost:3000/authorization-code/callback
並http://localhost:3000/
用於註銷重定向 URI。
Okta CLI 有什麼作用?
Okta CLI 將在您的 Okta 組織中創建一個 OIDC Web 應用程序。它將添加您指定的重定向 URI,並授予對 Everyone 組的訪問權限。完成後,您將看到如下輸出:
Okta application configuration has been written to: /path/to/app/.okta.env
運行cat .okta.env
(或type .okta.env
在 Windows 上)以查看您的應用的頒發者和憑據。
export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"
您的 Okta 域是您的發行人的第一部分,在/oauth2/default
.
注意:您還可以使用 Okta 管理控制台來創建您的應用程序。有關詳細信息,請參閱創建 Web 應用程序。
接下來,您將使用express-generator
應用程序生成器工具快速搭建應用程序。運行以下命令。
npx express-generator@4.16 --view=pug
現在你可以安裝你的包了。
npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
@prisma/client
用於從您的服務器代碼訪問數據庫。dotenv.env
從像 Okta CLI 生成的文件中讀取配置設置。passport
是 Node.js 的中間件,它足夠靈活,可以處理大多數身份驗證場景,包括 Okta。passport-openidconnect
是一個護照模塊,可讓您使用 OpenID Connect 進行身份驗證。express-session
需要護照。Express 應用程序使用這個包來支持會話。您的應用程序必須初始化會話支持才能使用護照。Tailwind CSS
是您將使用的 CSS 框架。如果您以前從未使用過 Tailwind,您可能想知道為什麼它是開發依賴項。這是因為 Tailwind 會根據您的視圖和配置動態構建您的 CSS 文件。稍後再談。prisma
是您正在使用的 ORM。這是一個開發依賴項,因為 Prisma 庫處理遷移、種子數據等,而@prisma/client
包在運行時在您的應用程序中使用。此時,您將初始化 Tailwind CSS 和 Prisma。您將配置它們以供稍後使用。
從 Prisma 開始並運行以下命令。
npx prisma init
此命令將添加一個名為prisma
您的應用程序的新文件夾。它還.env
使用一些默認配置添加一個名為您的項目的文件。用下面的代碼替換該文件的內容。
# 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="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}
在這個示例中,我使用了 SQLite,因為它簡單且緊湊。但是關於 Prisma 和大多數 ORM 的一個很酷的事情是它們可以支持許多數據庫。樣板代碼包括有關為其他數據庫服務器配置應用程序的註釋。隨意使用您覺得最舒服的東西並使用適當的連接字符串。
您的種子數據將使用該SEED_USER_NAME
設置。您要確保它與您登錄的 Okta 用戶名相同。這將允許應用程序將您的登錄用戶與您將播種到數據庫中的數據相關聯。
接下來,package.json
使用以下代碼更新您的文件。
{
"name": "workout-app",
"version": "0.0.0",
"private": true,
"prisma": {
"seed": "node prisma/seed.js"
},
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"@prisma/client": "^3.13.0",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^16.0.0",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"passport": "^0.5.2",
"passport-openidconnect": "^0.1.1",
"pug": "2.0.0-beta11"
},
"devDependencies": {
"prisma": "^3.13.0",
"tailwindcss": "^3.0.24"
}
}
這將添加prisma seed
您稍後需要的命令。
現在您可以初始化 Tailwind CSS。
npx tailwindcss init
您現在已經添加了一個新文件,名為tailwind.config.js
應用程序的根目錄。用下面的代碼替換該文件的內容。
module.exports = {
content: ["./views/**/*.pug"],
theme: {
extend: {},
},
plugins: [],
}
在這一步中,您告訴 Tailwind CSS 在哪裡可以找到您在應用程序中使用的類。您希望 Tailwind 查看.pug
您views
目錄中的文件。Tailwind CSS 具有高度可擴展性,從配置對象theme
和plugins
設置可以看出。對此的深入探討超出了本文的範圍,但我鼓勵您查看 Tailwind CSS 的網站以獲取更多信息。
下一個任務是為您的應用程序創建數據庫。步驟將涉及編寫架構、編寫一些種子數據、創建遷移和應用遷移,這也將為您的數據提供種子。
上面的prisma init
任務應該已經添加了一個名為schema.prisma
您的prisma
目錄的文件。用下面的代碼替換那裡的代碼。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "{yourDatabaseProvider}"
url = env("DATABASE_URL")
}
model WorkoutLog {
id Int @id @default(autoincrement())
userId Int
exercise String
amount Decimal
units String
date DateTime
minutes Int
calories Int
user User @relation(fields: [userId], references: [id])
}
model User {
id Int @id @default(autoincrement())
username String @unique
workoutLogs WorkoutLog[]
}
確保您替換為您正在使用的提供{yourDatabaseProvider}
程序。如您所見,該文件定義了您將存儲在數據庫中的對象和對象。具有有關用戶在特定日期鍛煉的基本信息,然後將該記錄與對象相關聯。您還可以從此文件中定義鍵、約束和索引。請注意,上的屬性具有由表定義的關係。UserWorkoutLogWorkoutLogUseruserWorkoutLoguserIdidUser
將文件添加到prisma
名為的目錄中seed.js
,並將以下代碼添加到其中。
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
require("dotenv").config();
async function main() {
const user = await prisma.user.upsert({
where: { username: process.env.SEED_USER_NAME },
update: {},
create: {
username: process.env.SEED_USER_NAME,
workoutLogs: {
create: [
{
exercise: "Running",
amount: 1,
units: "Miles",
date: new Date(2022, 1, 1),
minutes: 8,
calories: 100
},
{
exercise: "Running",
amount: 1.2,
units: "Miles",
date: new Date(2022, 1, 3),
minutes: 10,
calories: 120
},
{
exercise: "Running",
amount: 1.5,
units: "Miles",
date: new Date(2022, 1, 5),
minutes: 12,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 1),
minutes: 15,
calories: 100
},
{
exercise: "Heavy Bag",
amount: 6,
units: "Rounds",
date: new Date(2022, 1, 3),
minutes: 22,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 5),
minutes: 15,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 10,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 10,
calories: 100
},
],
},
},
});
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Prisma 知道使用seed.js
您添加到的命令中的文件package.json
。當您運行該命令時,此文件將插入數據。
您現在可以使用 Prisma CLI 添加和應用遷移。從應用程序目錄的根目錄運行以下命令。
npx prisma migrate dev --name init
CLI 有多種添加和應用遷移的方法。我建議您了解為您的環境管理遷移的最佳方式。上述方法是準備數據庫的最簡單、最快的方法。作為遷移過程的一部分,此命令seed
將從您的命令中查找package.json
並運行該命令。完成後,您的數據庫應該準備好使用充滿種子數據的數據庫。
現在您的數據庫已準備就緒,您可以將注意力轉向應用程序的核心。
首先,在名為的應用程序的根目錄中添加一個新文件,ensureLoggedIn.js
並向其中添加以下代碼。
function ensureLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login')
}
module.exports = ensureLoggedIn;
這個小中間件將確保用戶通過身份驗證。如果不是,您會將它們重定向到login
您將配置為使用 Okta 的路由。否則,您將允許用戶到達下一個屏幕。
接下來,您可以更新routes
目錄中的路線。首先,刪除users.js
,因為您不會使用它。將代碼替換為index.js
以下內容。
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});
module.exports = router;
在這裡,您將傳遞一個屬性,isAuthenticated
以便您的佈局頁面可以正確顯示登錄或註銷按鈕。
接下來,使用以下代碼在routes
目錄中添加一個文件。dashboard.js
var express = require("express");
var router = express.Router();
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
const ensureLoggedIn = require("../ensureLoggedIn");
/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
const username = req.user.username;
const dbUser = await prisma.user.findUnique({
where: {
username: username,
},
include: {
workoutLogs: true,
},
});
res.render("dashboard", {
title: "The Workout Tracker - Dashboard",
isAuthenticated: req.isAuthenticated(),
user: dbUser
});
});
module.exports = router;
這條路線有很多魔法。首先,您正在使用ensureLoggedIn
中間件來保護路由。然後您可以從請求中提取用戶名並使用 Prisma 客戶端在數據庫中查找用戶。由於用戶有其鍛煉日誌的屬性,您可以將用戶作為模型傳遞,並在您將創建的 pug 視圖上解析日誌。這也將授予對用戶名的查看訪問權限。當然,workoutLogs
如果這種方法更適合您的工作流程,您可以直接查詢表格並包含分頁和搜索等功能。
現在,將代碼替換為app.js
以下內容。
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');
const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');
var app = express();
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient();
require('dotenv').config({path:'./.okta.env'});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(session({
secret: 'CanYouLookTheOtherWay',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;
// set up passport
passport.use('oidc', new Strategy({
issuer: OKTA_OAUTH2_ISSUER,
authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
clientID: OKTA_OAUTH2_CLIENT_ID,
clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/authorization-code/callback',
scope: 'openid profile'
}, (issuer, profile, done) => {
return done(null, profile);
}));
passport.serializeUser((user, next) => {
next(null, user);
});
passport.deserializeUser((obj, next) => {
next(null, obj);
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/login', passport.authenticate('oidc'));
app.use('/authorization-code/callback',
passport.authenticate('oidc', { failureRedirect: '/error' }),
(req, res, next) => {
res.redirect('/dashboard');
}
);
app.post('/logout', (req, res) => {
req.logout();
req.session.destroy();
res.redirect('/');
});
app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
您更新了路由以正確使用dashboard
路由以及 Okta 將使用的任何登錄或註銷路由。.okta.env
您還可以使用 Okta CLI 在文件中生成的值設置通行證以使用 Okta 。
默認情況下,express-generator
會為您添加一些視圖。您將需要編輯它們並添加一個新的。首先打開目錄layout.pug
中的文件views
並將其中的代碼替換為以下內容。
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/output.css')
body
nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
.w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
.text-sm(class='lg:flex-grow')
div
if(isAuthenticated)
form(action="logout" method="POST")
button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
else
a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
block content
你會注意到關於這個文件的一些事情。首先,課程非常具體和豐富。這是 Tailwind CSS 的典型特徵。Tailwind CSS 不是定義特定元素的外觀,而是努力定義特定樣式的外觀,然後您可以從元素中添加和刪除樣式以適應所需的外觀。
您將看到的另一件事是登錄/註銷按鈕,該按鈕將根據isAuthenticated
屬性進行更改。
接下來,您可以index.pug
使用以下內容進行更新。
extends layout
block content
.py-12.bg-white
.max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
div.c(class='lg:text-center')
p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
| A small demo built with
a(href="https://expressjs.com/") Express on
a(href="https://nodejs.org/en/") Node.JS
| Secured with
a(href="https://developer.okta.com/signup") Okta.
br
| This app uses
a(href="https://tailwindcss.com/") Tailwind CSS
| for its CSS framework and
a(href="https://www.prisma.io/") Prisma.JS
| for its ORM.
這裡沒什麼好說的。只是一個帶有更多 Tailwind CSS 和一些所用技術鏈接的啟動頁面。
最後,dashboard.pug
使用以下代碼添加文件。
extends layout
block content
.flex.flex-wrap
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-green-600
i.fa.fa-wallet.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0)
span.text-green-500
i.fas.fa-caret-up
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-pink-600
i.fas.fa-users.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Calories Burned
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)
span.text-pink-500
i.fas.fa-exchange-alt
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-yellow-600
i.fas.fa-user-plus.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Days Worked
p.font-bold.text-3xl
span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
span.text-yellow-600
i.fas.fa-caret-up
.w-full.p-6
.bg-white.border-transparent.rounded-lg.shadow-xl
.bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
h2.font-bold.uppercase.text-gray-600 Workout Log
.p-5
table.w-full.p-5.text-gray-700
thead
tr
th.text-left.text-blue-900 Date
th.text-left.text-blue-900 Execise
th.text-left.text-blue-900 Amount
th.text-left.text-blue-900 Time
th.text-left.text-blue-900 Calories Burned
tbody
each log in user.workoutLogs.sort((a,b) => a.date - b.date)
tr
td=log.date.toLocaleDateString()
td=log.exercise
td=log.amount + ' ' + log.units
td=log.minutes + ' Minutes'
td=log.calories
如您之前所見,此頁面受到保護。因此,它要求您有一個用戶才能訪問此頁面。該應用程序將提取用戶詳細信息和鍛煉日誌,並將它們形成頁面頂部的摘要,其中包含基於用戶執行的鍛煉的幾個日誌表。
此過程的最後一部分是創建應用程序將使用的實際 CSS 文件。在您的public/stylesheets
目錄中,您將看到一個名為style.css
. 用下面的代碼替換該文件中的代碼。
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}
這在很大程度上是相同的。但是,您正在為 Tailwind 的每個層導入指令。
接下來,您可以編譯您在 pug 文件中使用的類和您剛剛修改的樣式表,以創建一個新的 CSS 文件。運行以下命令。
npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch
現在您可以打開output.css
文件並查看完整的 Tailwind CSS 代碼。這裡的--watch
參數對本地開發很有幫助。output.css
這將在您每次更改其中一個佈局時重建文件。如果您不想進行任何更改,可以忽略該參數。
您現在已準備好啟動您的應用程序。運行以下命令。
npm run start
啟動應用程序後,您應該能夠導航到http://localhost:3000/並查看您的主頁。
使用屏幕頂部的登錄按鈕並導航到 Okta 登錄頁面。登錄後,您將被重定向回您的網站,在那裡您將能夠看到您的儀表板頁面。
在本文中,您學習瞭如何使用 Prisma 為您的數據庫定義模式。然後,您學習瞭如何從該架構創建遷移並為您的數據庫播種。您看到 Prisma 可以與最流行的數據庫選項一起使用,包括 SQL Server、MySql、MongoDB 等。
您還學習瞭如何使用 Tailwind CSS 構建 CSS 文件。我們介紹了一個簡單的順風配置以及 Tailwind CLI 如何使用您的代碼來構建其 CSS 文件。
最後,您學習瞭如何使用護照保護您的快速申請。
鏈接:https ://developer.okta.com/blog/2022/07/25/express-prisma-tailwind
#tailwindcss #express #prisma
1658991189
Prisma là một công cụ ORM (ánh xạ đối tượng-quan hệ) cho Node.js sử dụng TypeScript. Phần mềm tích hợp với nhiều cơ sở dữ liệu phổ biến nhất hiện nay, bao gồm MySQL, SQL Server, SQLite và MongoDB, đồng thời nhấn mạnh một lược đồ có thể đọc được với máy khách cơ sở dữ liệu kiểu an toàn. Prisma cũng bao gồm các tính năng khác như di chuyển, dữ liệu hạt giống và trình duyệt cơ sở dữ liệu ảo.
Trong dự án này, bạn sẽ sử dụng Prisma để kết nối ứng dụng Express của mình với máy chủ cơ sở dữ liệu. Bạn sẽ xây dựng một lược đồ để lập mô hình ứng dụng theo dõi tập luyện. Sau đó, bạn sẽ tạo một số dữ liệu gốc và sử dụng Prisma để chạy quá trình di chuyển và khởi tạo cơ sở dữ liệu của bạn. Cuối cùng, bạn sẽ tạo ứng dụng web bằng Pug và Tailwind CSS để xây dựng giao diện ứng dụng.
Điều kiện tiên quyết
Hướng dẫn này sử dụng các công nghệ sau nhưng không yêu cầu bất kỳ kinh nghiệm nào trước đó:
Nếu bạn muốn bỏ qua hướng dẫn và xem dự án được xây dựng đầy đủ, bạn có thể xem nó trên GitHub .
Tạo một thư mục mới cho ứng dụng của bạn. Sử dụng cd
lệnh để điều hướng đến thư mục đó.
Trước khi bắt đầu, bạn cần có tài khoản nhà phát triển Okta miễn phí. Cài đặt Okta CLI và chạy okta register
để đăng ký tài khoản mới. Nếu bạn đã có tài khoản, hãy chạy okta login
. Sau đó, chạy okta apps create
. Chọn tên ứng dụng mặc định hoặc thay đổi nó khi bạn thấy phù hợp. Chọn Web và nhấn Enter .
Chọn Khác . Sau đó, thay đổi URI chuyển hướng thành http://localhost:3000/authorization-code/callback
và sử dụng http://localhost:3000/
cho URI chuyển hướng đăng xuất.
Okta CLI làm gì?
Okta CLI sẽ tạo Ứng dụng web OIDC trong Tổ chức Okta của bạn. Nó sẽ thêm các URI chuyển hướng mà bạn đã chỉ định và cấp quyền truy cập vào nhóm Mọi người. Bạn sẽ thấy kết quả như sau khi hoàn tất:
Okta application configuration has been written to: /path/to/app/.okta.env
Chạy cat .okta.env
(hoặc type .okta.env
trên Windows) để xem nhà phát hành và thông tin đăng nhập cho ứng dụng của bạn.
export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"
Miền Okta của bạn là phần đầu tiên của công ty phát hành của bạn, trước đây /oauth2/default
.
LƯU Ý : Bạn cũng có thể sử dụng Bảng điều khiển dành cho quản trị viên Okta để tạo ứng dụng của mình. Xem Tạo ứng dụng web để biết thêm thông tin.
Tiếp theo, bạn sẽ sử express-generator
dụng công cụ tạo ứng dụng để tạo nhanh ứng dụng của mình. Chạy lệnh sau.
npx express-generator@4.16 --view=pug
Bây giờ bạn có thể cài đặt các gói của mình.
npm i @prisma/client@3.13.0
npm i dotenv@16.0.0
npm i passport@0.5.2
npm i passport-openidconnect@0.1.1
npm i express-session@1.17
npm i -D tailwindcss@3.0.24
npm i -D prisma@3.13.0
@prisma/client
được sử dụng để truy cập cơ sở dữ liệu từ mã máy chủ của bạn.dotenv
đọc cài đặt cấu hình từ .env
các tệp như tệp được tạo bởi Okta CLI.passport
là một phần mềm trung gian cho Node.js đủ linh hoạt để xử lý hầu hết các tình huống xác thực, bao gồm cả Okta. passport-openidconnect
là một mô-đun dành cho hộ chiếu cho phép bạn xác thực với OpenID Connect.express-session
là bắt buộc đối với hộ chiếu. Các ứng dụng Express sử dụng gói này để hỗ trợ phiên. Ứng dụng của bạn phải phức tạp hóa hỗ trợ phiên để sử dụng hộ chiếu.Tailwind CSS
là khung CSS mà bạn sẽ sử dụng. Nếu bạn chưa từng sử dụng Tailwind trước đây, bạn có thể thắc mắc tại sao nó lại phụ thuộc vào sự phát triển. Điều này là do Tailwind sẽ tự động tạo các tệp CSS của bạn từ các chế độ xem và cấu hình của bạn. Thêm về điều đó sau.prisma
là ORM bạn đang sử dụng. Đây là sự phụ thuộc của nhà phát triển vì thư viện Prisma xử lý các quá trình di chuyển, dữ liệu hạt giống, v.v., trong khi @prisma/client
gói được sử dụng trong ứng dụng của bạn lúc chạy.Tại thời điểm này, bạn sẽ khởi tạo Tailwind CSS và Prisma. Bạn sẽ cấu hình chúng để sử dụng sau này.
Bắt đầu với Prisma và chạy lệnh sau.
npx prisma init
Lệnh này sẽ thêm một thư mục mới có tên prisma
vào ứng dụng của bạn. Nó cũng thêm một tệp được gọi .env
vào dự án của bạn với một số cấu hình mặc định. Thay thế nội dung của tệp đó bằng mã bên dưới.
# 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="file:./dev.db"
SEED_USER_NAME={yourOktaUserName}
Trong ví dụ này, tôi đã sử dụng SQLite vì nó dễ dàng và nhỏ gọn. Nhưng một trong những điều thú vị về Prisma và hầu hết các ORM là chúng có thể hỗ trợ nhiều cơ sở dữ liệu. Mã boilerplate bao gồm các nhận xét về việc định cấu hình ứng dụng của bạn cho các máy chủ cơ sở dữ liệu khác. Hãy thoải mái sử dụng những gì bạn cảm thấy thoải mái nhất và sử dụng chuỗi kết nối thích hợp.
Dữ liệu hạt giống của bạn sẽ sử dụng SEED_USER_NAME
cài đặt này. Bạn muốn đảm bảo rằng nó giống với tên người dùng Okta mà bạn đăng nhập. Điều này sẽ cho phép ứng dụng liên kết người dùng đã đăng nhập của bạn với dữ liệu bạn sẽ đưa vào cơ sở dữ liệu của mình.
Tiếp theo, cập nhật package.json
tệp của bạn bằng mã sau.
{
"name": "workout-app",
"version": "0.0.0",
"private": true,
"prisma": {
"seed": "node prisma/seed.js"
},
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"@prisma/client": "^3.13.0",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^16.0.0",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"passport": "^0.5.2",
"passport-openidconnect": "^0.1.1",
"pug": "2.0.0-beta11"
},
"devDependencies": {
"prisma": "^3.13.0",
"tailwindcss": "^3.0.24"
}
}
Thao tác này sẽ thêm prisma seed
lệnh bạn cần sau đó.
Bây giờ bạn có thể khởi tạo CSS Tailwind.
npx tailwindcss init
Bây giờ bạn đã thêm một tệp mới được gọi tailwind.config.js
vào thư mục gốc của ứng dụng của bạn. Thay thế nội dung của tệp đó bằng mã bên dưới.
module.exports = {
content: ["./views/**/*.pug"],
theme: {
extend: {},
},
plugins: [],
}
Trong bước này, bạn cho Tailwind CSS biết nơi tìm các lớp bạn đã sử dụng trong ứng dụng của mình. Bạn muốn Tailwind tìm kiếm các .pug
tệp trong thư mục của bạn views
. Tailwind CSS có khả năng mở rộng cao, như được thấy bởi đối tượng cấu hình theme
và plugins
cài đặt. Tìm hiểu sâu về vấn đề này nằm ngoài phạm vi của bài viết này, nhưng tôi khuyến khích bạn nên xem trang web của Tailwind CSS để biết thêm thông tin.
Nhiệm vụ tiếp theo là tạo cơ sở dữ liệu cho ứng dụng của bạn. Các bước sẽ liên quan đến việc viết lược đồ, viết một số dữ liệu gốc, tạo quá trình di chuyển và áp dụng quá trình di chuyển, cũng sẽ tạo ra dữ liệu của bạn.
Tác prisma init
vụ từ trên đáng lẽ phải thêm một tệp được gọi schema.prisma
vào thư mục của bạn prisma
. Thay thế mã ở đó bằng mã dưới đây.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "{yourDatabaseProvider}"
url = env("DATABASE_URL")
}
model WorkoutLog {
id Int @id @default(autoincrement())
userId Int
exercise String
amount Decimal
units String
date DateTime
minutes Int
calories Int
user User @relation(fields: [userId], references: [id])
}
model User {
id Int @id @default(autoincrement())
username String @unique
workoutLogs WorkoutLog[]
}
Đảm bảo rằng bạn thay thế {yourDatabaseProvider}
bằng nhà cung cấp bạn đang sử dụng . Như bạn có thể thấy, tệp này xác định đối tượng User
và WorkoutLog
đối tượng mà bạn sẽ lưu trữ trong cơ sở dữ liệu. Có WorkoutLog
thông tin cơ bản về quá trình tập luyện của người dùng vào một ngày cụ thể và sau đó liên kết bản ghi đó với User
đối tượng. Bạn cũng có thể xác định khóa, ràng buộc và chỉ số từ tệp này. Lưu ý rằng thuộc user
tính trên WorkoutLog
có một mối quan hệ được xác định bởi mục userId
của id
bảng User
.
Thêm tệp vào thư mục prisma
có tên seed.js
và thêm mã sau vào thư mục đó.
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
require("dotenv").config();
async function main() {
const user = await prisma.user.upsert({
where: { username: process.env.SEED_USER_NAME },
update: {},
create: {
username: process.env.SEED_USER_NAME,
workoutLogs: {
create: [
{
exercise: "Running",
amount: 1,
units: "Miles",
date: new Date(2022, 1, 1),
minutes: 8,
calories: 100
},
{
exercise: "Running",
amount: 1.2,
units: "Miles",
date: new Date(2022, 1, 3),
minutes: 10,
calories: 120
},
{
exercise: "Running",
amount: 1.5,
units: "Miles",
date: new Date(2022, 1, 5),
minutes: 12,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 1),
minutes: 15,
calories: 100
},
{
exercise: "Heavy Bag",
amount: 6,
units: "Rounds",
date: new Date(2022, 1, 3),
minutes: 22,
calories: 150
},
{
exercise: "Heavy Bag",
amount: 4,
units: "Rounds",
date: new Date(2022, 1, 5),
minutes: 15,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 2),
minutes: 10,
calories: 100
},
{
exercise: "Situps",
amount: 50,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 5,
calories: 50
},
{
exercise: "Pushups",
amount: 100,
units: "Reps",
date: new Date(2022, 1, 4),
minutes: 10,
calories: 100
},
],
},
},
});
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Prisma biết sử dụng seed.js
tệp từ lệnh bạn đã thêm vào package.json
. Tệp này sẽ chèn dữ liệu khi bạn chạy lệnh đó.
Bây giờ bạn có thể thêm và áp dụng việc di chuyển bằng Prisma CLI. Từ thư mục gốc của thư mục ứng dụng của bạn, hãy chạy lệnh sau.
npx prisma migrate dev --name init
CLI có một số cách để thêm và áp dụng việc di chuyển. Tôi khuyên bạn nên hiểu cách tốt nhất để quản lý việc di chuyển cho môi trường của bạn. Phương pháp trên là cách dễ nhất và nhanh nhất để chuẩn bị cơ sở dữ liệu của bạn. Là một phần của quá trình di chuyển, lệnh này sẽ tìm kiếm seed
lệnh từ của bạn package.json
và chạy lệnh đó. Sau khi hoàn tất, cơ sở dữ liệu của bạn sẽ sẵn sàng với một cơ sở dữ liệu đầy đủ dữ liệu hạt giống để làm việc.
Bây giờ cơ sở dữ liệu của bạn đã sẵn sàng, bạn có thể chuyển sự chú ý của mình đến phần cốt lõi của ứng dụng của bạn.
Đầu tiên, thêm một tệp mới trong thư mục gốc của ứng dụng của bạn được gọi ensureLoggedIn.js
và thêm mã sau vào tệp đó.
function ensureLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login')
}
module.exports = ensureLoggedIn;
Phần mềm trung gian nhỏ này sẽ đảm bảo người dùng được xác thực. Nếu không, bạn sẽ chuyển hướng chúng đến login
tuyến đường mà bạn sẽ định cấu hình để sử dụng Okta. Nếu không, bạn sẽ cho phép người dùng đến màn hình tiếp theo.
Tiếp theo, bạn có thể cập nhật các tuyến đường của mình trong routes
danh bạ. Đầu tiên, hãy xóa users.js
vì bạn sẽ không sử dụng nó. Thay thế mã bằng mã index.js
sau.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'The Workout Tracker - Home', isAuthenticated: req.isAuthenticated() });
});
module.exports = router;
Tại đây, bạn sẽ chuyển một thuộc tính để isAuthenticated
trang bố cục của bạn có thể hiển thị đúng nút đăng nhập hoặc đăng xuất.
Tiếp theo, thêm một tệp trong routes
thư mục dashboard.js
với mã sau.
var express = require("express");
var router = express.Router();
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
const ensureLoggedIn = require("../ensureLoggedIn");
/* GET home page. */
router.get("/", ensureLoggedIn, async function (req, res) {
const username = req.user.username;
const dbUser = await prisma.user.findUnique({
where: {
username: username,
},
include: {
workoutLogs: true,
},
});
res.render("dashboard", {
title: "The Workout Tracker - Dashboard",
isAuthenticated: req.isAuthenticated(),
user: dbUser
});
});
module.exports = router;
Có rất nhiều phép thuật trong tuyến đường này. Đầu tiên, bạn đang sử dụng ensureLoggedIn
phần mềm trung gian để bảo vệ tuyến đường. Sau đó, bạn có thể trích xuất tên người dùng từ yêu cầu và tra cứu người dùng trong cơ sở dữ liệu bằng ứng dụng khách Prisma. Vì người dùng có một thuộc tính cho nhật ký tập luyện của mình, bạn có thể chuyển người dùng làm mô hình và phân tích cú pháp các nhật ký trên chế độ xem pug mà bạn sẽ tạo. Điều này cũng sẽ cung cấp cho chế độ xem quyền truy cập vào tên của người dùng. Tất nhiên, bạn có thể truy vấn workoutLogs
bảng trực tiếp và bao gồm các tính năng như phân trang và tìm kiếm nếu cách tiếp cận này phù hợp hơn với quy trình làm việc của bạn.
Bây giờ, thay thế mã bằng mã app.js
sau.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var dashboardRouter = require('./routes/dashboard');
const session = require('express-session');
const passport = require('passport');
const { Strategy } = require('passport-openidconnect');
var app = express();
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient();
require('dotenv').config({path:'./.okta.env'});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(session({
secret: 'CanYouLookTheOtherWay',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
const {OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, OKTA_OAUTH2_ISSUER} = process.env;
// set up passport
passport.use('oidc', new Strategy({
issuer: OKTA_OAUTH2_ISSUER,
authorizationURL: `${OKTA_OAUTH2_ISSUER}/v1/authorize`,
tokenURL: `${OKTA_OAUTH2_ISSUER}/v1/token`,
userInfoURL: `${OKTA_OAUTH2_ISSUER}/v1/userinfo`,
clientID: OKTA_OAUTH2_CLIENT_ID,
clientSecret: OKTA_OAUTH2_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/authorization-code/callback',
scope: 'openid profile'
}, (issuer, profile, done) => {
return done(null, profile);
}));
passport.serializeUser((user, next) => {
next(null, user);
});
passport.deserializeUser((obj, next) => {
next(null, obj);
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/login', passport.authenticate('oidc'));
app.use('/authorization-code/callback',
passport.authenticate('oidc', { failureRedirect: '/error' }),
(req, res, next) => {
res.redirect('/dashboard');
}
);
app.post('/logout', (req, res) => {
req.logout();
req.session.destroy();
res.redirect('/');
});
app.use('/', indexRouter);
app.use('/dashboard', dashboardRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Bạn đã cập nhật các tuyến đường của mình để sử dụng đúng các dashboard
tuyến đường cũng như mọi tuyến đường đăng nhập hoặc đăng xuất mà Okta sẽ sử dụng. Bạn cũng thiết lập hộ chiếu để sử dụng Okta bằng các giá trị Okta CLI được tạo trong .okta.env
tệp.
Theo mặc định, express-generator
sẽ thêm một vài chế độ xem cho bạn. Bạn sẽ cần phải chỉnh sửa chúng và thêm một cái mới của riêng bạn. Bắt đầu bằng cách mở layout.pug
tệp trong thư mục views
và thay thế mã ở đó bằng mã sau.
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/output.css')
body
nav.flex.items-center.justify-between.flex-wrap.bg-teal-500.p-6.flex.items-center.flex-shrink-0.text-white.mr-6
span.font-semibold.text-xl.tracking-tight.pr-2 The Workout Tracker
.w-full.block.flex-grow(class='lg:flex lg:items-center lg:w-auto')
.text-sm(class='lg:flex-grow')
div
if(isAuthenticated)
form(action="logout" method="POST")
button(type="submit").inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4 Logout
else
a.inline-block.text-sm.px-4.py-2.leading-none.border.rounded.text-white.border-white.mt-4(href='Login' class='hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0') Login
block content
Bạn sẽ nhận thấy một số điều về tệp này. Đầu tiên, các lớp học rất cụ thể và phong phú. Đây là điển hình của Tailwind CSS. Thay vì xác định giao diện của một phần tử cụ thể, Tailwind CSS cố gắng xác định kiểu dáng cụ thể trông như thế nào, sau đó bạn có thể thêm và xóa kiểu khỏi các phần tử để phù hợp với giao diện mong muốn.
Điều khác bạn sẽ thấy là nút Đăng nhập / Đăng xuất sẽ thay đổi dựa trên thuộc isAuthenticated
tính.
Tiếp theo, bạn có thể cập nhật index.pug
những điều sau.
extends layout
block content
.py-12.bg-white
.max-w-7xl.mx-auto.px-4(class='sm:px-6 lg:px-8')
div.c(class='lg:text-center')
p.mt-2.text-3xl.leading-8.font-extrabold.tracking-tight.text-gray-900(class='sm:text-4xl') The Workout Tracker
p.mt-4.max-w-2xl.text-xl.text-gray-500(class='lg:mx-auto')
| A small demo built with
a(href="https://expressjs.com/") Express on
a(href="https://nodejs.org/en/") Node.JS
| Secured with
a(href="https://developer.okta.com/signup") Okta.
br
| This app uses
a(href="https://tailwindcss.com/") Tailwind CSS
| for its CSS framework and
a(href="https://www.prisma.io/") Prisma.JS
| for its ORM.
Không có nhiều điều để nói ở đây. Chỉ là một trang giới thiệu với một số CSS Tailwind khác và một số liên kết đến các công nghệ được sử dụng.
Cuối cùng, thêm một tệp cho dashboard.pug
với mã sau.
extends layout
block content
.flex.flex-wrap
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-green-200.to-green-100.border-b-4.border-green-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-green-600
i.fa.fa-wallet.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Minutes Worked
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.minutes }, 0)
span.text-green-500
i.fas.fa-caret-up
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-pink-200.to-pink-100.border-b-4.border-pink-500.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-pink-600
i.fas.fa-users.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Calories Burned
p.font-bold.text-3xl
span= user.workoutLogs.reduce(function(total, item) { return total + item.calories }, 0)
span.text-pink-500
i.fas.fa-exchange-alt
.w-full.p-6(class='md:w-1/2 xl:w-1/3')
.bg-gradient-to-b.from-yellow-200.to-yellow-100.border-b-4.border-yellow-600.rounded-lg.shadow-xl.p-5
.flex.flex-row.items-center
.flex-shrink.pr-4
.rounded-full.p-5.bg-yellow-600
i.fas.fa-user-plus.fa-2x.fa-inverse
.flex-1.text-right(class='md:text-center')
h2.font-bold.uppercase.text-gray-600 Total Days Worked
p.font-bold.text-3xl
span= user.workoutLogs.map(r => r.date).filter((date, i, self) => self.findIndex(d => d.getTime() === date.getTime()) === i).length
span.text-yellow-600
i.fas.fa-caret-up
.w-full.p-6
.bg-white.border-transparent.rounded-lg.shadow-xl
.bg-gradient-to-b.from-gray-300.to-gray-100.uppercase.text-gray-800.border-b-2.border-gray-300.rounded-tl-lg.rounded-tr-lg.p-2
h2.font-bold.uppercase.text-gray-600 Workout Log
.p-5
table.w-full.p-5.text-gray-700
thead
tr
th.text-left.text-blue-900 Date
th.text-left.text-blue-900 Execise
th.text-left.text-blue-900 Amount
th.text-left.text-blue-900 Time
th.text-left.text-blue-900 Calories Burned
tbody
each log in user.workoutLogs.sort((a,b) => a.date - b.date)
tr
td=log.date.toLocaleDateString()
td=log.exercise
td=log.amount + ' ' + log.units
td=log.minutes + ' Minutes'
td=log.calories
Như bạn đã thấy trước đây, trang này đã được bảo vệ. Do đó, nó yêu cầu bạn phải có một người dùng để truy cập trang này. Ứng dụng sẽ kéo chi tiết người dùng và nhật ký tập luyện và tạo thành một bản tóm tắt ở đầu trang với một vài bảng nhật ký dựa trên các bài tập mà người dùng đã thực hiện.
Phần cuối cùng của quá trình này là tạo tệp CSS thực tế mà ứng dụng của bạn sẽ sử dụng. Trong thư mục của bạn public/stylesheets
, bạn sẽ thấy một tệp có tên style.css
. Thay thế mã trong tệp đó bằng mã bên dưới.
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}
Điều này phần lớn giống nhau. Tuy nhiên, bạn đang nhập các lệnh cho từng lớp của Tailwind.
Tiếp theo, bạn có thể biên dịch các lớp bạn đã sử dụng trong tệp pug của mình và bảng định kiểu bạn vừa sửa đổi để tạo tệp CSS mới. Chạy lệnh sau.
npx tailwindcss -i ./public/stylesheets/style.css -o ./public/stylesheets/output.css --watch
Bây giờ bạn có thể mở output.css
tệp và xem mã Tailwind CSS hoàn chỉnh. Tham --watch
số ở đây rất hữu ích cho sự phát triển của địa phương. Thao tác này sẽ xây dựng lại output.css
tệp mỗi khi bạn thay đổi một trong các bố cục của mình. Bạn có thể bỏ qua tham số đó nếu bạn không muốn thực hiện bất kỳ thay đổi nào.
Bây giờ bạn đã sẵn sàng để bắt đầu ứng dụng của mình. Chạy lệnh sau.
npm run start
Khi ứng dụng của bạn được khởi động, bạn sẽ có thể điều hướng đến http: // localhost: 3000 / và xem trang chủ của mình.
Sử dụng nút đăng nhập ở đầu màn hình và điều hướng ra trang đăng nhập Okta. Sau khi đăng nhập, bạn sẽ được chuyển hướng trở lại trang web của mình, nơi bạn có thể xem trang bảng điều khiển của mình.
Trong bài viết này, bạn đã học cách sử dụng Prisma để xác định lược đồ cho cơ sở dữ liệu của mình. Sau đó, bạn đã học cách tạo sự di chuyển từ lược đồ đó và khởi tạo cơ sở dữ liệu của mình. Bạn thấy rằng Prisma có thể được sử dụng với các tùy chọn cơ sở dữ liệu phổ biến nhất, bao gồm SQL Server, MySql, MongoDB, v.v.
Bạn cũng đã học cách tạo tệp CSS bằng Tailwind CSS. Chúng ta đã xem qua cấu hình tailwind đơn giản và cách Tailwind CLI sử dụng mã của bạn để tạo tệp CSS của nó.
Cuối cùng, bạn đã học được cách bảo mật đơn xin cấp tốc của mình bằng hộ chiếu.
Liên kết: https://developer.okta.com/blog/2022/07/25/express-prisma-tailwind
#tailwindcss #express #prisma
1658658600
As migrações de esquema de banco de dados são algumas das tarefas mais frequentes e importantes executadas pelos desenvolvedores, especialmente à medida que os requisitos do produto evoluem. Se essas migrações não forem executadas corretamente, podem ocorrer consequências desastrosas, como perda de dados e inconsistências de esquema, levando a um estado inconsistente do banco de dados.
No entanto, eles são bastante tediosos para executar e gerenciar. Para resolver esse problema, o Prisma ORM criou uma ferramenta chamada Prisma Migrate que fornece mecanismos para realizar e gerenciar migrações de esquemas de banco de dados sem problemas.
Neste artigo, teremos uma visão prática detalhada de como realizar migrações de esquema de banco de dados usando o Prisma ORM. Começaremos projetando um esquema básico de um modelo de aplicativo de mídia social e trabalharemos fazendo alterações simples e complexas nesse esquema. Em essência, veremos como a funcionalidade integrada do Prisma (chamada Prisma Migrate) torna a criação e o gerenciamento de migrações de banco de dados o mais simples possível.
Devido aos aspectos práticos nas seções posteriores deste artigo, você deve ter conhecimento prático de Node.js e JavaScript. Você também precisará instalar e configurar o PostgreSQL localmente, seguindo o guia do site oficial .
Prisma é um ORM (Object Relational Mapper) que fornece uma abstração de alto nível sobre consultas de banco de dados brutos e funcionalidades de gerenciamento de banco de dados. Ele fornece uma API de tipo seguro e classes JavaScript para realizar consultas comuns ao banco de dados.
No contexto deste artigo, o Prisma fornece um sistema de migração robusto chamado Prisma Migrate, que simplifica o processo de criação e gerenciamento de migrações de esquema de banco de dados. Veremos como aproveitar esse sistema de migração mais tarde, mas antes disso, vamos fazer uma rápida atualização sobre migrações de esquema de banco de dados de uma perspectiva geral de banco de dados.
As migrações de esquema de banco de dados são uma maneira de gerenciar alterações incrementais feitas em um esquema de banco de dados existente, normalmente devido a alterações de requisitos ou correção de erros iniciais de design. Essas alterações em um banco de dados relacional incluem adicionar ou remover colunas e tabelas, alterar tipos de dados específicos para determinadas colunas, entre outras coisas.
Normalmente, realizamos migrações de esquema usando um arquivo de migração que contém código SQL com as alterações necessárias. Pode ser bastante desafiador gerenciar manualmente os arquivos de migração, especialmente quando o banco de dados contém dados do usuário do mundo real. No entanto, o Prisma fornece mecanismos para tornar esse processo menos assustador e arriscado do que o normal, usando um recurso específico chamado Prisma Migrate.
Com isso coberto, vamos montar um projeto e entrar no cerne do artigo. Primeiro, vá para um diretório adequado e execute o seguinte comando no terminal:
npm init
O comando acima inicializa o diretório e cria um package.json
arquivo. Quando estiver concluído, execute o seguinte comando para adicionar o pacote Prisma como uma dependência de desenvolvimento:
npm install prisma --save-dev
Então, podemos inicializar um projeto Prisma executando outro comando no terminal:
npx prisma init
O comando acima inicializa um projeto Prisma criando um diretório chamado prisma
e um arquivo dentro desse diretório chamado schema.prisma
. Este arquivo é onde a maior parte do trabalho acontecerá, e chegaremos a isso daqui a pouco.
Em seguida, criaremos um esquema de modelo de um aplicativo de mídia social simples; esse esquema servirá como base para fazer alterações incrementais, imitando o que normalmente encontraremos em um cenário do mundo real.
Deve-se mencionar mais uma vez que, embora esse esquema seja muito básico, ele nos ajudará a entender como podemos realizar migrações de esquema no Prisma. Com isso dito, vá em frente e cole o seguinte código no schema.prisma
arquivo, e nós o analisaremos logo em seguida:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Users {
id String @id @db.Uuid @default(uuid())
fullName String
email String @unique
password String
bio String
@@map("users")
}
model Posts {
id String @id @db.Uuid @default(uuid())
userId String
postId String
content String
@@map("posts")
}
model Followers {
id String @id @db.Uuid @default(uuid())
userId String
followerId String
@@map("followers")
}
model Likes {
id String @id @db.Uuid @default(uuid())
userId String
postId String
@@map("likes")
}
model Comments {
id String @id @db.Uuid @default(uuid())
userId String
postId String
content String
@@map("comments")
}
O arquivo de esquema acima contém um esquema do aplicativo projetado usando uma sintaxe especial fornecida pelo Prisma. Os modelos são mapeados diretamente para tabelas no banco de dados relacional subjacente.
Uma das principais vantagens desse método é que o Prisma restringe a modelagem de dados à camada de banco de dados, diferente de outros ORMs que exigem algum nível de modelagem no nível do aplicativo. Pode haver problemas de design óbvios com o esquema acima, como nenhum relacionamento entre tabelas; isso foi feito de propósito.
Nas seções a seguir, veremos como podemos corrigir esses problemas fazendo alterações simples de esquema (adicionando colunas, alterando tipos de dados e estabelecendo relacionamentos) a alterações de esquema complexas (fazendo alterações no esquema com dados existentes no banco de dados).
Para aplicar o esquema ao banco de dados, execute este comando no diretório do terminal:
npx prisma migrate dev --name init
Quando rodamos
npx prisma init
anteriormente, o Prisma gerava um.env
arquivo para gerenciar as variáveis ambientais da aplicação. Este arquivo contém uma variável de ambiente chamadaDATABASE_URL
. Substitua o valor por uma URL para se conectar à sua conexão de banco de dados Postgres local (ou remota). Para referência, conectei-me ao meu banco de dados local usando isto:postgresql://postgres:admin@localhost:5433/prisma-migration?schema=public
Quando esse comando terminar de ser executado, poderemos ver as tabelas e colunas reais existentes nesse banco de dados. O Prisma também gera uma pasta chamada migrations
no mesmo diretório do schema.prisma
arquivo. Se abrirmos o arquivo de migração recém-criado, poderemos ver as instruções SQL brutas geradas automaticamente pelo Prisma. Sob o capô, o Prisma converteu o conteúdo do schema.prisma
arquivo em instruções SQL brutas e aplicou isso ao banco de dados.
Vamos em frente e fazer alterações no esquema do banco de dados.
Em seguida, faremos alterações relativamente diretas no esquema e as aplicaremos.
Vamos supor que os requisitos de negócios evoluíram e queremos que os usuários de nosso aplicativo tenham nomes de usuário exclusivos. Para implementar essa alteração no nível do esquema, vá até o schema.prisma
arquivo e adicione a linha destacada no snippet de código abaixo, e nós a analisaremos depois:
model Users {
...
username String @unique
...
}
No snippet de código acima, adicionamos uma nova coluna à Users
tabela e aplicamos uma restrição exclusiva, garantindo que vários usuários não possam ter o mesmo nome de usuário. Para aplicar essa alteração, tudo o que precisamos fazer é dizer ao Prisma para sincronizar nossas alterações com o banco de dados; podemos fazer isso facilmente executando o seguinte código no terminal:
npx prisma migrate dev --name added_username_column
Agora vamos ver uma mudança mais complexa em nosso esquema. Está claro que não há relacionamentos entre as tabelas em nosso esquema devido ao “design ruim”, então vamos ver como podemos fazer essas alterações. Vamos definir um relacionamento um-para-muitos entre a Users
tabela e a Posts
tabela de forma que um usuário possa ter muitos posts. Mude para o esquema para implementar isso:
model Users {
id String @id @db.Uuid @default(uuid())
fullName String
username String @unique
email String @unique
password String
bio String
posts Posts[]
@@map("users")
}
model Posts {
id String @id @db.Uuid @default(uuid())
postId String
content String
user Users @relation(fields: [userId], references: [id])
userId String // (used in the `@relation` attribute above)
@@map("posts")
}
Mais uma vez, tudo o que precisamos fazer é executar o seguinte comando no terminal e o Prisma Migrate sincroniza automaticamente essas alterações com o banco de dados:
npx prisma migrate dev --name connect_users_posts_tables
Essa é a simplicidade de aplicar atualizações de esquema com o Prisma Migrate. No entanto, há coisas importantes a serem observadas. Depois que o Prisma aplica os arquivos de migração, eles nunca devem ser editados ou excluídos manualmente. Fazer isso pode levar a inconsistências, forçando o Prisma a fornecer um prompt solicitando a redefinição do banco de dados, o que pode levar à perda de dados. Em resumo, podemos acabar com históricos incompatíveis entre as versões dos arquivos de migração. Além disso, migrate dev
só deve ser executado em ambientes de desenvolvimento, pois isso pode levar a problemas se executado em ambientes de produção.
Nas duas seções anteriores, vimos como fazer alterações em nosso esquema e com que facilidade o Prisma faz isso. No entanto, só examinamos isso sem ter nenhum dado no banco de dados. Torna-se um pouco mais complicado quando temos dados, porque as alterações erradas podem levar à perda de dados ou inconsistências de dados no banco de dados. Nesta seção, examinaremos brevemente como aplicar alterações em nosso esquema nesses cenários.
O primeiro passo é adicionar um ou mais usuários ao aplicativo, adicionar alguns usuários manualmente ou executar o seguinte SQL:
INSERT INTO users (id, "fullName", email, password, bio, username)
VALUES ('7f93880a-a8c3-4d4f-b3b3-369aa89a73fa', 'John Doe', 'john@yahoo.com', 'johndoe', 'Im a software developer', 'johndoe');
Com isso feito, digamos mais adiante, decidimos renomear a coluna bio na tabela do usuário para biography
; aparentemente podemos fazer isso simplesmente alterando o User
modelo no prisma.schema
arquivo assim:
model Users {
...
biography String
...
}
Agora, se tentarmos confirmar essa alteração usando o Prisma Migrate, obteremos um erro nesta linha:
Erro:Encontramos alterações que não podem ser executadas: Etapa 0 Adicionada a biografia da coluna necessária à tabela de usuários sem um valor padrão. Há 1 linha nesta tabela, não é possível executar esta etapa. Você pode usar prisma migrate dev --create-only para criar o arquivo de migração e modificá-lo manualmente para resolver os problemas subjacentes. Em seguida, execute prisma migrate dev para aplicá-lo e verificar se funciona.
Isso ocorre principalmente porque temos uma ou mais linhas na users
tabela e a migração gerada pelo Prisma é potencialmente destrutiva. Para evitar isso, o Prisma solicita que criemos o arquivo de migração sem sincronizá-lo com o banco de dados imediatamente. Podemos fazer isso executando:
npx prisma migrate dev --name update-biography --create-only
Com isso, um arquivo de migração é gerado, mas não aplicado imediatamente. Vamos dar uma olhada nisso; abra o arquivo de migração e ele deve se parecer com algo assim:
ALTER TABLE "users" DROP COLUMN "bio",
ADD COLUMN "biography" TEXT NOT NULL;
Pode ser óbvio por que isso é um problema – o SQL acima descarta a bio
coluna e adiciona uma nova coluna chamada biography
, mas há pelo menos uma linha com um valor nessa coluna, o que significa que perderemos todos os dados nessa coluna. Assim, o Prisma lança um aviso e nos permite atualizar o arquivo manualmente.
Nesse caso, para resolver o problema, podemos atualizar ou reescrever facilmente o SQL no arquivo de migração para isso:
ALTER TABLE "users" RENAME COLUMN "bio" TO "biography";
A nova instrução SQL nos permite renomear a coluna sem perda de dados. Com isso, tudo o que precisamos fazer é dizer ao Prisma para sincronizar o arquivo de migração executando o usual:
npx prisma migrate dev
E voilà, nosso arquivo de esquema atualizado é sincronizado com o banco de dados.
No momento, o Prisma Migrate não oferece suporte a provedores de banco de dados MongoDB; isso é apenas uma limitação se você planeja usar o MongoDB.
Em ambientes de desenvolvimento, às vezes o Prisma Migrate pode enviar um prompt para redefinir o banco de dados; isso, infelizmente, leva à perda de dados em seu ambiente de desenvolvimento. Se tivermos arquivos de semente, isso não será um grande problema, pois o banco de dados pode ser propagado novamente com dados. É importante observar que esse prompt para redefinir o banco de dados não ocorre em ambientes de produção.
Por fim, o Prisma Migrate não permite aplicar migrações para diferentes provedores de banco de dados especificados no schema.prisma
arquivo, ou seja, se criarmos um arquivo de migração para um provedor PostgreSQL em ambientes dev, não podemos aplicar essa migração para um provedor MySQL em ambientes de produção.
Neste artigo, passamos pelo processo de execução de migrações de esquema usando o Prisma Migrate. Fizemos isso modelando uma plataforma de mídia social simples, fazendo alterações incrementais no esquema e usando as funcionalidades fornecidas pelo Prisma Migrate para criar e aplicar uma migração automaticamente.
Fonte: https://blog.logrocket.com/effortless-database-schema-migration-prisma/